自动内存管理
自动内存管理的基本概念
动态内存
程序根据自身的运行需要对内存进行的一个申请(像在 C 语言中我们就可以通过 mlloc()/alloc() 进行内存的分配)
自动内存管理(有程序的运行时进行管理,即垃圾回收)
1. 不需要手动进行内存的管理,我们就可以专注与业务代码的实现
2. 保证内存的使用的正确与安全,避免出现(double-free problem,use-after-free problem)的情况,可能会造成安全问题以及程序崩溃
主要的工作
1. 为新的对象分配空间
2. 寻找正在使用的对象
3. free 已经消亡对象的空间
方式
1. Tracing garbage collection
2. Generational GC
3. Reference counting
Some Definition for Automatic Memory Management
-
Mutator
- 业务线程,分配新对象,修改对象指向关系
-
Collector
- GC线程,找到存活对象,free 回收死亡对象的空间
-
Serial GC
- 只存在一个 collecor
-
Parallerl GC
- 支持多个 collectors 同时进行回收的 GC 算法
-
Concurrent GC
- mutator(s) & collector(s) 同时运行(需要知道对象的指向关系的转变,已标记对象指向的对象也需要进行标记)
评价 GC 算法的一些标准
- Safety(安全性)
- 不能回收存活的对象
- 基本要求
- Throughput(吞吐率)
- 在 GC 上花费的时间
- Pause time(暂停时间)
- Stop the world(STW)
- 业务是否感知
- Space overhead(内存开销)
- GC 元数据开销
常见的两种 GC 技术
Tracing garbage collection(追踪垃圾回收)
- 条件: 指针引用关系不可达
- 步骤
- 标记根对象
- 静态变量
- 全局常量
- 常量
- 线程栈
- ...
- 标记: 找到可达的对象
- 求指针对象指向关系的传递闭包
- 从根对象出发,寻找可达的对象
- 清理: 所有不可达对象
- 将存活对象拷贝到另外的区域(copying GC)
- 将死亡对象空间标记为"可分配的"(Mark-sweep GC)
- 对对象进行移动整理(Mark-compact GC)
- 标记根对象
Note: 对于一些声明周期不同的对象,可能采取不同的标记和清理策略
Generational GC(分代 GC)
1. 分代假说(Generationalhyupothesis): most objects die young
2. Intuition: 一些对象分配出来后很快就不再使用
3. 对象有年龄(经历过 GC 的次数)
4. 目的: 对于年轻或者是老的对象采取不同的 GC 策略, 降低整体内存的开销
5. 不同的年龄对象处于 Heap 的不同区域(Young/Old Generation)
- Young generation
- 常规对象分配
- 存活对象少,可以采用 copying collection
- GC 吞吐率高
- Old generation
- 对象一直存活,反复复制会使开销较大
- 可以采用 mark-sweep collection
Reference counting(引用计数)
1. 每个对象有一个相关联的引用数目
2. 对象存活的条件: Reference counts > 0
优点
1. 内存管理的操作被平摊到程序执行过程中
2. 内存管理不需要了解 runtime 的实现细节(C++ 智能指针 smart pointer)
缺点
1. 维护计数开销大(需要使用原子操作保证计数的原子性和可见性)
2. 对于环形的数据结构无法进行回收(当然还有其它方式对它进行回收)
3. 内存开销: 每一个对象引入额外的内存空间用于引用数目
4. 回收内存可能会造成暂停(当一个节点下挂着许多节点时)