GC基础概念
GC基础概念
参考资料
目录
- 程序内存分配 TODO 堆栈 TODO
- GC的定义
- GC做了什么
- 有GC跟没有GC的对比
- GC常见概念
- GC常见算法分析
专业词汇
- 内存泄露
- 悬垂指针
GC的定义
垃圾的回收
GC: Garbage Collection 的简称,垃圾回收. GC 把程序不用的内存空间视为垃圾
GC要做两件事
- 找到内存空间里的垃圾
- 回收垃圾,让程序员能再次利用这部分空间
GC的好处
没有 GC的世界
在没有 GC 的世界里,程序员必须自己手动进行内存管理,必须清楚地确保必要的内存 空间,释放不要的内存空间。
没有GC的世界:
- 需要手动进行内存管理
手动管理内存带来的问题:
- 内存泄露: 忘记释放内存空间, 则会导致该内存空间发生泄露
- 悬垂指针(dangling pointer): 释放内存空间时,如果忘记初始化指向释放对象的内存空间的指针,这个指针就会一直指向释放完毕的内存空间
- 错误释放空间
有GC的世界
- GC的好处: GC会自动管理内存。编码时可以无需关心内存管理, 把精力集中在更本 质的编程工作上。
GC的历史
// TODO
在学习GC之前
对象/头/域
- 对象: 在GC中, 对象表示"通过应用程序利用的数据的集合”。对象配置在内存空间里。GC 根据情况将配置好的对象进行移动或销毁操作。因此,对
- 对象 由头(header)和域(field)构成
- 头: 对象中保存对象本身信息的部分, 主要包含以下信息:
- 对象的大小
- 对象的种类
- 作用:
- 利用对象的种类和大小 判别内存中存储的对象的边界
- 根据不同的GC算法, 事先存储GC运行需要的信息
域: 对象使用者在对象中可以访问的部分。 域中数据类型:
- 指针: 指向内存空间中某块区域的值
- 非指针: 直接使用值本身。如数值、字符、布尔值等
- 对象使用者会引用或者替换对象的域值。对象使用者无法直接修改头的信息。
指针
GC 是根据对象的指针指向去搜寻其他对象的。另一方面,GC 对非指针不进行任何操作
- 在大多数语言处理程序中,指针都默认指向对象的首地址
Mutator (这里总觉得还差一个直接的图, 来标识应用程序与GC)
- mutator即是"应用程序"本身,
- mutator 实际进行的操作:
- 生成对象
- 更新指针
- 应用程序会改变GC对象间的引用关系
堆
堆指的是用于动态(也就是执行程序时)存放对象的内存空间
GC 是管理堆中已分配对象的机制。在开始执行 mutator 前,GC 要分配用于堆的内存空间。 一旦开始执行 mutator,程序就会按照 mutator 的要求在堆中存放对象。等到堆被对象占满后, GC 就会启动,从而分配可用空间。如果不能分配足够的可用空间,一般情况下我们就要扩大堆
- HEAP_SIZE: 堆的大小
- $heap_start: 指向堆首地址的指针
- $heap_end: 指向堆末尾下一个地址的指针 (这里可以理解为不包含?)
活动对象/非活动对象
- 将分配到内存空间中的对象中那些能通过 mutator 引用的对象称为“活动对象”
- 把分配到堆中那些不能通过程序引用的对象称为“非活动对象”。
- 即非活动对象)我们就称为“垃圾”
- 非活动对象无法转换为活动对象, 因为无法通过mutator重新找到它
分配
分配(allocation)指的是在内存空间中分配对象 当 mutator 需要新对象时,就会向分配 器(allocator)申请一个大小合适的空间。分配器则在堆的可用空间中找寻满足要求的空间, 返回给 mutator。 当堆被所有活动对象占满时,就算运行 GC 也无法分配可用空间
- 销毁至今为止的所有计算结果,输出错误信息
- 扩大堆,分配可用空间
分块
分块(chunk)在 GC 的世界里指的是为利用对象而事先准备出来的空间 初始状态下,堆被一个大的分块所占据。 然后,程序会根据 mutator 的要求把这个分块分割成合适的大小,作为(活动)对象使用。 活动对象不久后会转化为垃圾被回收。此时,这部分被回收的内存空间再次成为分块,为下次 被利用做准备。也就是说,内存里的各个区块都重复着分块→活动对象→垃圾(非活动对象)→ 分块→ ……这样的过程
根
- 在GC中, 根是指向对象的指针的“起点” 部分.能通过 mutator 直接引用的空间 调用栈、寄存器以及全局变量空间都是根
GC评价标准
吞吐量 • 最大暂停时间 • 堆使用效率 • 访问的局部性
吞吐量
从一般意义上来讲,吞吐量(throughput)指的是“在单位时间内的处理能力” GC吞吐量 = 堆的大小/GC花费时间 在 mutator 整个执行过程中,GC 一共启动了 3 次,我们把花费的时间分别设为 A、B、C。 也就是说,GC 总共花费的时间为(A + B + C)。 堆大小是 HEAP_SIZE GC 的吞吐量为 HEAP_SIZE /(A + B + C) 判断各算法吞吐量的好坏时不能一概 而论 同一 GC 算法,其吞吐量也是受 mutator 的动作左右的。评价 GC 算法 的吞吐量时,有必要把 mutator 的动作也考虑在内
最大暂停时间
GC 执行过程中令 mutator可能会 暂停执行 最 大暂停时间指的是“因执行 GC 而暂停执行 mutator 的最长时间”。 最大暂停时间 & 吞吐量不可兼得
堆使用效率
左右堆使用效率的因素有两个: 一个是头的大小,另一个是堆的用法 头的大小: 在堆中堆放的信息越多,GC 的效率也就越高,吞吐量也就随之得到改善。 但毋庸置疑,头越小越好。因此为了执行 GC,需要把在头中堆放的信息控制在最小限度。 根据堆的用法,堆使用效率也会出现巨大的差异。举个例子,GC 复制算法中将 堆二等分,每次只使用一半,交替进行,因此总是只能利用堆的一半。相对而言,GC 标记 - 清除算法和引用计数法就能利用整个堆 堆使用效率和吞吐量,以及最大暂停时间不可兼得。简单地说就是:可用的堆越 大,GC 运行越快;相反,越想有效地利用有限的堆,GC 花费的时间就越长
访问的局部性
具有引用关系的对象之间通常很可能存在连续访问的情况,把具有引用关系的对象安排在堆 中较近的位置,就能提高在缓存中读取到想利用的数据的概率
把所有的数据都放在内存里,当 CPU 访问数据时,仅把 要使用的数据从内存读取到缓存。与此同时,我们还将它附近的所有数据都读取到缓存中, 从而压缩读取数据所需要的时间