这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记
自动内存管理
Auto memory management: 自动内存管理
- 被分配的存储空间早期需要程序员手动回收,现在系统可以自动管理内存,即垃圾回收机制,极大避免了内存泄漏
- 保证内存使用的正确性和安全性:double-free problem,use-after-free problem
GC三个任务:
- 为新对象分配空间
- 找到存活对象
- 回收死亡对象的内存空间
相关概念
-
Grabage collction: 垃圾回收GC
- 对不再使用的内存资源进行定位和自动回收的行为就被称为垃圾回收。
-
Mutator: 业务线程,分配新对象,修改对象指向关系
- 内存错误:内存泄漏、悬空指针等
- 垃圾回收技术就是为了解决内存错误而产生的自动内存管理技术
- Mutator是修改器线程,指任何可以修改Heap的线程,一般指应用业务线程
-
Collector: GC 线程,找到存活对象,回收死亡对象的内存空间
-
Serial GC:只有一个collector
-
Concurrent GC: 并发 GC
- 指回收线程Collector和业务线程Mutator可同时运行
- 必须感知对象指向关系的改变
-
Parallel GC: 并行 GC
- 指支持多个collector回收线程同时进行垃圾回收工作
评价GC算法
- 安全性:不能回收存活对象(基本要求)
- 吞吐率:1-GC时间/程序运行总时间
- 暂停时间:stop the world(STW),业务是否感知
- 内存开销:GC元数据开销
Tracing garbage collection: 追踪垃圾回收
追踪式算法的核心思想是判断一个对象是否可达,因为一旦这个对象不可达就可以立刻被 GC 回收了
-
标记根对象:静态变量、全局变量、常量、线程栈等
-
Copying GC: 复制对象 GC
- 把某个空间里的活动对象复制到其他空间,把原空间里的所有对象都回收掉
-
Mark-sweep GC: 标记-清理 GC
- 标记阶段就是把所有的活动对象都做上标记的阶段:遍历,常用深度优先搜索
- 清除阶段就是把那些没有标记的对象回收的阶段:遍历,内存合并
-
Mark-compact GC: 标记-压缩 GC
- 标记阶段标记活动对象
- 压缩阶段把未标记的对象压缩到堆的其中一块,按顺序放
根据对象的生命周期,使用不同的标记和清理策略
Reference counting: 引用计数机制RC
- 采用引用计数来管理对象的内存,当需要持有一个对象时,使它的引用计数 +1;
- 当不需要持有一个对象的时候,使它的引用计数 -1;
- 当一个对象的引用计数为 0,该对象就会被销毁。
优点
- 内存管理的操作被平摊到程序运行中:指针传递的过程中进行引用计数的增减
- 不需要了解 runtime 的细节:因为不需要标记 GC roots,因此不需要知道哪里是全局变量、线程栈等
缺点
-
维护引用计数的开销大,因为对象可能会被多线程访问,对引用计数的修改需要原子操作保证原子性和可见性
-
无法回收环形数据结构——weak reference
-
内存开销:每个对象都引入额外存储空间存储引用计数
-
虽然引用计数的操作被平摊到程序运行过程中,但是回收大的数据结构依然可能引发暂停
Generational GC: 分代 GC
-
分代假说:most objects die young
- 弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的,生命很短
- 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡
-
每个对象都有年龄:经历过GC的次数
-
Intuition:很多对象在分配出来后很快就不再使用了
-
对年轻和老年的对象,制定不同的GC策略,降低整体内存管理的开销
-
不同年龄的对象处于heap的不同区域
-
Young generation: 年轻代(刚分配出来的对象)
- 由于存活对象比较少,可以采用Copying GC
-
Old generation: 老年代(在一次GC后把年轻代中存活的对象上升到老年代)
- 对象趋向于一直活着,反复复制开销较大,可以采用Mark-Sweep GC