open-courses
  • 公开课笔记
  • CMU 15-445/645 Database Systems
    • Relational Data Model
    • Advanced SQL
    • Database Storage
    • Buffer Pools
    • Hash Tables
    • Tree Indexes
    • Index Concurrency Control
    • Query Processing
    • Sorting&Aggregations
    • Join Algorithms
    • Query Optimization
    • Parallel Execution
    • Embedded Database Logic
    • Concurrency Control Theory
    • Two Phase Locking
    • Timestamp Ordering Concurrency Control
    • Multi-Version Concurrency Control
    • Logging Schemes
    • Database Recovery
    • Introduction to Distributed Databases
    • Distributed OLTP Databases
    • Distributed OLAP Databases
  • UCB - CS162
    • OS intro
    • Introduction to the Process
    • Processes, Fork, I/O, Files
    • I/O Continued, Sockets, Networking
    • Concurrency: Processes & Threads
    • Cooperating Threads, Synchronization
    • Semaphores, Condition Variables, Readers/Writers
    • Scheduling
    • Resource Contention & Deadlock
    • Address Translation, Caching
    • File System (18,19,20)
    • Distributed Systems, Networking, TCP/IP, RPC (21,22)
    • Distributed Storage, Key-Value Stores, Security (23)
    • Security & Cloud Computing (24)
    • Topic: Ensuring Data Reaches Disk
  • MIT - 6.006
    • Sequence and Set Interface
    • Data Structure for Dynamic Sequence Interface
    • Computation Complexity
    • Algorithms and Computation
    • Structure Of Computation
    • Graph & Search
    • Tree & Search
    • Weighted Shortest Paths
    • String Matching, Karp-Rabin
    • Priority Queue Interface & Implementation
    • Dictionary Problem & Implementation
    • Sorting
    • Dynamic Programming
    • Backtracking
    • Self-Balancing Tree
  • MIT - 6.824
    • 2PC & 3PC
    • Introduction and MapReduce
    • RPC and Threads
    • Primary/Backup Replication
    • Lab: Primary/Backup Key/Value Service
    • Google File System (GFS)
    • Raft
    • Lab: Raft - Leader Election
    • Lab: Raft - Log Replication
  • Stanford-CS107
    • 原始数据类型及相互转化
    • 指鹿为马
    • 泛型函数
    • 泛型栈
    • 运行时内存结构
    • 从 C 到汇编
    • 函数的活动记录
    • C 与 C++ 代码生成
    • 编译的预处理过程
    • 编译的链接过程
    • 函数的活动记录续、并发
    • 从顺序到并发和并行
    • 信号量与多线程 1
    • 信号量与多线程 2
    • 复杂多线程问题
    • 函数式编程 - Scheme 1
    • 函数式编程 - Scheme 2
    • 函数式编程 - Scheme 3
    • 函数式编程 - Scheme 4
    • 函数式编程 - Scheme 5
    • Python 基础
  • MIT - 6.001 - SICP
    • 什么是程序
    • 程序抽象
    • 替代模型
    • 时间/空间复杂度
    • 数据抽象
    • 高阶函数
    • Symbol
    • 数据驱动编程与防御式编程
    • 数据抽象中的效率与可读性
    • 数据修改
    • 环境模型
    • 面向对象-消息传递
    • 面向对象 - Scheme 实现
    • 构建 Scheme 解释器
    • Eval-Apply Loop
    • Normal Order (Lazy) Evaluation
    • 通用机
    • 寄存器机器
    • 子程序、栈与递归
    • 在寄存器机器中执行
    • 内存管理
  • MIT - 6.046
    • Randomized Algorithms
    • Skip Lists
  • System Design
    • Twitter
    • Cache Consistency & Coherence
  • DDIA 笔记
    • Replication
    • Transactions
    • The Trouble with Distributed Systems
    • Consistency & Consensus
  • Papers We Love
    • Consistent Hashing and Random Trees (1997)
    • Dynamic Hash Tables (1988)
    • LFU Implementation With O(1) Complexity (2010)
    • Time, Clocks, and the Ordering of Events in a Distributed System (1978)
    • Dapper, a Large-Scale Distributed Systems Tracing Infrastructure (2010)
    • Gorilla: A Fast, Scalable, In-Memory Time Series Database (2015)
  • Release It 笔记
    • Anti-patterns & Patterns in Microservice Architecture
  • Database Design
    • Log Structured Merge (LSM) Tree & Usages in KV Stores
    • Prometheus
Powered by GitBook
On this page
  1. MIT - 6.001 - SICP

内存管理

二十四课

回顾 register machine

(图1)

在前几课介绍的 register machine 如上图所示,它主要由三个部分组成:

  • registers: 用来存储各种 procedures 执行时所需要的参数、环境及输出的结果等数据

  • stack: 用来临时存储将被修改的 register 中的数据以便在 subroutine 执行完后恢复环境继续执行

  • heap: 用来存储各种各样的具体数据结构的地方。对于 scheme 来说就是存储所有 list 的地方,无论是表达式还是数据都是 list。

本节介绍的就是 heap 及其所依赖的技术。

Abstraction

Vector

内存支持数据的随机存取,即可以在常数级别的时间复杂度内存取内存中任意位置上的数据,这可以被抽象为 Vector 模型,如下图所示:

(图2)

其中在 Scheme、Register Machine 中相应的操作也如上图所示。

Stack

我们也很容易使用 Vector 来实现 Stack:用一个 stack pointer (SP) 指向当前 stack 的顶部,每当 save 的时候就往 vector 增大的方向移动 SP;每当 restore 的时候就往 vector 减小的方向移动 SP:

(save <reg>)
; becomes
(assign sp (op +) (reg sp) (cons 1))
(perform (op vector-set!) (reg stack)
          (reg sp) (reg <reg>))


(restore <reg>)
; becomes
(assign <reg> (op vector-ref) (reg stack) (reg sp))
(assign sp (op - ) (reg sp) (cons 1))

Heap

在 register machine 中,heap 就是一堆 cons,它们分别由 car 和 cdr 构成。为了支持 cons,我们可以使用两个平行等长的 vector 来模拟,第一个 vector 叫作 the-cars,第二个 vector 叫作 the-cdrs,它们共用 base index,如下图所示:

(图3)

car & cdr

cons 的 selectors 操作,即 car 和 cdr 如下文所示:

; CAR
(assign <reg> (op car) <pair>)
; becomes
(assign <reg> (op vector-ref)
              (reg the-cars) <pair>))

; CDR
(assign <reg> (op cdr) <pair>)
; becomes
(assign <reg> (op vector-ref)
              (reg the-cdrs) <pair>)
; where <pair> is a cell offset into the-cars & the-cdrs vectors

cons

heap 不仅要支持 cons 数据的获取,还需要支持 cons 的分配与回收,即 heap 的内存管理。首先我们需要新增一个 register --- free,它指向当前尚未分配的最近一个 cell,当分配操作被执行时,free 指针指向的 cell 被分配出去,free 指针则指向下一个尚未分配的 cell,整个过程示意图如下所示:

(图4)

cons 的执行过程如下所示:

; CONS
(assign <rename> (op cons) <val-1> <val-2>)
; becomes
(perform (op vector-set!)
         (reg the-cars) (reg free) <val-1>)
(perform (op vector-set!)
         (reg the-cdrs) (reg free) <val-2>)
(assign <regname> (reg free))
(assign free (op +) (reg free) (const 1))

那么我们如何区别 cons cell 中储存的不同类型的数据呢?比如 null、integer、boolean、指向其它 cell 的指针等等。我们可以拿出 (car cell)、(cdr cell) 中数据的一部分 bits 来存储数据的类型,剩下的 bits 用于存取具体的数据内容,如下图所示:

(图5)

举例:

(define a (list 4 7 6))
(cons a a)

利用刚才的 vector 抽象,我们足以支持以上语句中的 list 数据创建,如下图所示:

(图6)

cell 的回收:Garbage Collection

如果内存足够大,我们可以不断地分配 cells,但我们知道内存空间无论再怎么大也是有限度的,当空间不足时,我们就需要把不用的 heap 空间回收,即所谓垃圾回收 (Garbage Collection)。

垃圾生成 (Creation of Garbage)

垃圾就是在当下之前分配的、直到程序运行结束时都不可能再被使用到的内存空间。一个简单的垃圾生成的例子如下图所示:

(图7)

垃圾检测 (Detection of Garbage)

与程序之后运行有关的数据都存储在 registers、stack 以及 global environment 中,因此只要把它们中指向的 heap cons cells,以及这些 cells 指向的其它 cells 保留,剩下的没有被引用到的 cells 就是垃圾。

垃圾回收法1 --- Mark/Sweep Garbage Collection

Mark & Sweep 顾名思义,分为 Mark 和 Sweep 两个阶段。Mark 阶段 Garbage Collector 会顺着 registers、stack 和 global environment 出发,把所有被直接或间接引用到的 cells 都 mark 成 1;Sweep 阶段在 Mark 阶段完成后,遍历一遍被分配的 cells,把所有没有被 mark 成 1 的 cells 全部回收。接着刚才的 the-cars 和 the-cdrs 模型,我们新增一个 the-marks vector,就可以分别完成 mark 和 sweep,举例如下图所示:

(图8)

Mark 阶段:从 registers、stack 和 global environment 中的 c 出发,找到直接引用 4 以及间接引用 0、1,于是它们被 mark 成 1。

Sweep 阶段:将没有被 mark 成 1 的 2、3 回收。

Mark/Sweep 的示例代码如下:

; Mark Phase
(define (mark object)
  (cond ((not (pair? object)) #f)
        ((not (= 1 (vector-ref the-marks object)))
         (vector-set! the-marks object 1)
         (mark (car object))
         (mark (cdr object)))))
; For a pair, "object" is an integer offset denoting the cell location

; Sweep Phase
(define (sweep i)
  (cond ((not (= i size))
         (cond ((= 1 (vector-ref the-marks i))
                (vector-set! the-marks i 0))
               (else (set-cdr! (int-to-pointer i) free)
                     (set! free (int-to-pointer i))))
         (sweep (+ i 1)))))

(define (gc)
  (mark root)
  (sweep 0))

垃圾回收法2 --- Stop & Copy Garbage Collection

使用 Stop & Copy 策略时,Garbage Collector 将可支配内存空间分为两半,只将一半用于分配。当用于分配的一半分配完后,同样从 registers、stack 和 global environment 出发遍历所有直接引用和间接引用的空间,将其中的信息复制到另一半未分配的空间中去,并保持它们之间的引用关系。相较于 Mark & Sweep 策略,Stop & Copy 需要两倍内存空间,但后者可以利用复制的过程来做碎片整理。

小结

垃圾回收最早需要程序员手动完成,但由于这个过程即使是素质过硬的程序员也在所难免会出现错误,这种错误难以检查、发现,将它交给程序管理,确实是一种进步。但作为站在巨人肩上的程序员,了解一下你为什么能站在巨人的肩膀上,为什么能写着在天上飞的语言还是很有必要的。

参考

Previous在寄存器机器中执行NextRandomized Algorithms

Last updated 6 years ago

Youtube: SICP-2004-Lecture-24