目录

GC基础概念

GC基础概念

参考资料

目录

  • 程序内存分配 TODO 堆栈 TODO
  • GC的定义
  • GC做了什么
  • 有GC跟没有GC的对比
  • GC常见概念
  • GC常见算法分析

专业词汇

  • 内存泄露
  • 悬垂指针

GC的定义

垃圾的回收

GC: Garbage Collection 的简称,垃圾回收. GC 把程序不用的内存空间视为垃圾

GC要做两件事

  1. 找到内存空间里的垃圾
  2. 回收垃圾,让程序员能再次利用这部分空间

GC的好处

没有 GC的世界

在没有 GC 的世界里,程序员必须自己手动进行内存管理,必须清楚地确保必要的内存 空间,释放不要的内存空间。

没有GC的世界:

  1. 需要手动进行内存管理

手动管理内存带来的问题:

  1. 内存泄露: 忘记释放内存空间, 则会导致该内存空间发生泄露
  2. 悬垂指针(dangling pointer): 释放内存空间时,如果忘记初始化指向释放对象的内存空间的指针,这个指针就会一直指向释放完毕的内存空间
  3. 错误释放空间

有GC的世界

  • GC的好处: GC会自动管理内存。编码时可以无需关心内存管理, 把精力集中在更本 质的编程工作上。

GC的历史

// TODO

在学习GC之前

对象/头/域

  • 对象: 在GC中, 对象表示"通过应用程序利用的数据的集合”。对象配置在内存空间里。GC 根据情况将配置好的对象进行移动或销毁操作。因此,对
  • 对象 由头(header)和域(field)构成
  • 头: 对象中保存对象本身信息的部分, 主要包含以下信息:
    • 对象的大小
    • 对象的种类
  • 作用:
    • 利用对象的种类和大小 判别内存中存储的对象的边界
    • 根据不同的GC算法, 事先存储GC运行需要的信息 https://static.pangzicaicai.com/picgo/202102161503482021-02-16-15-03-49.png

域: 对象使用者在对象中可以访问的部分。 域中数据类型:

  • 指针: 指向内存空间中某块区域的值
  • 非指针: 直接使用值本身。如数值、字符、布尔值等
  • 对象使用者会引用或者替换对象的域值。对象使用者无法直接修改头的信息。

指针

GC 是根据对象的指针指向去搜寻其他对象的。另一方面,GC 对非指针不进行任何操作 https://static.pangzicaicai.com/picgo/202102161520322021-02-16-15-20-32.png

  • 在大多数语言处理程序中,指针都默认指向对象的首地址

Mutator (这里总觉得还差一个直接的图, 来标识应用程序与GC)

  • mutator即是"应用程序"本身,
  • mutator 实际进行的操作:
    • 生成对象
    • 更新指针
  • 应用程序会改变GC对象间的引用关系

堆指的是用于动态(也就是执行程序时)存放对象的内存空间

GC 是管理堆中已分配对象的机制。在开始执行 mutator 前,GC 要分配用于堆的内存空间。 一旦开始执行 mutator,程序就会按照 mutator 的要求在堆中存放对象。等到堆被对象占满后, GC 就会启动,从而分配可用空间。如果不能分配足够的可用空间,一般情况下我们就要扩大堆

https://static.pangzicaicai.com/picgo/202102161632372021-02-16-16-32-38.png

  • HEAP_SIZE: 堆的大小
  • $heap_start: 指向堆首地址的指针
  • $heap_end: 指向堆末尾下一个地址的指针 (这里可以理解为不包含?)

活动对象/非活动对象

  • 将分配到内存空间中的对象中那些能通过 mutator 引用的对象称为“活动对象”
  • 把分配到堆中那些不能通过程序引用的对象称为“非活动对象”。
  • 即非活动对象)我们就称为“垃圾”
  • 非活动对象无法转换为活动对象, 因为无法通过mutator重新找到它

https://static.pangzicaicai.com/picgo/202102161648242021-02-16-16-48-25.png

分配

分配(allocation)指的是在内存空间中分配对象 当 mutator 需要新对象时,就会向分配 器(allocator)申请一个大小合适的空间。分配器则在堆的可用空间中找寻满足要求的空间, 返回给 mutator。 当堆被所有活动对象占满时,就算运行 GC 也无法分配可用空间

  1. 销毁至今为止的所有计算结果,输出错误信息
  2. 扩大堆,分配可用空间

分块

分块(chunk)在 GC 的世界里指的是为利用对象而事先准备出来的空间 初始状态下,堆被一个大的分块所占据。 然后,程序会根据 mutator 的要求把这个分块分割成合适的大小,作为(活动)对象使用。 活动对象不久后会转化为垃圾被回收。此时,这部分被回收的内存空间再次成为分块,为下次 被利用做准备。也就是说,内存里的各个区块都重复着分块→活动对象→垃圾(非活动对象)→ 分块→ ……这样的过程

  • 在GC中, 根是指向对象的指针的“起点” 部分.能通过 mutator 直接引用的空间 调用栈、寄存器以及全局变量空间都是根

https://static.pangzicaicai.com/picgo/202102161712212021-02-16-17-12-21.png

GC评价标准

吞吐量 • 最大暂停时间 • 堆使用效率 • 访问的局部性

吞吐量

从一般意义上来讲,吞吐量(throughput)指的是“在单位时间内的处理能力” https://static.pangzicaicai.com/picgo/202102161719382021-02-16-17-19-38.png 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 花费的时间就越长

访问的局部性

https://static.pangzicaicai.com/picgo/202102161739252021-02-16-17-39-26.png 具有引用关系的对象之间通常很可能存在连续访问的情况,把具有引用关系的对象安排在堆 中较近的位置,就能提高在缓存中读取到想利用的数据的概率

把所有的数据都放在内存里,当 CPU 访问数据时,仅把 要使用的数据从内存读取到缓存。与此同时,我们还将它附近的所有数据都读取到缓存中, 从而压缩读取数据所需要的时间