1.自动内存管理
自动内存管理其实指的是系统的垃圾回收(Garbage Collection):由程序语言运行时系统回收动态内存。
交由系统回收的好处是:
避免手动内存管理,能够专注与实现业务逻辑
保证内存的使用的正确性和安全性,避免出现如内存重复释放问题(double-free problem),内存释放后使用问题(use-after-free problem)
GC的主要三个任务:
- 为新对象分配空间
- 找到存活对象
- 回收死亡对象的内存空间
相关概念
- Mutator:业务线程,分配新对象,修改对象的指向关系
- Collector:GC线程,找到存活对象,回收死亡对象的内存空间
- Serial GC:串行GC,只有一个collector。这种回收方式在存在多个mutator时会暂停它们的线程,然后由一个collector进行回收
- Parallel GC:并行GC,和串行类似,支持多个collectors同时回收的GC算法
- Concurrent GC:mutator(s)和collector(s)可以同时执行
评价GC算法
我们可以通过以下几个方面来评价GC算法的好坏程度:
安全性(Safety):不应回收存活的对象,这是最基本的要求
吞吐率(Throughput):除开GC消耗时间后的运行时间与程序执行总时间的占比 ,这个占比越大越好
$$
1- \frac{GC时间}{程序执行总时间}
$$暂停时间(Pause time):业务线程因为GC导致暂停的花费时间,这个时间越短越好
内存开销(Space overhead):GC的元数据开销
追踪垃圾回收
通过根对象的引用链来标记可达对象使其不被回收,将其余不可达的对象视为垃圾进行回收清理。
常见的回收不可达对象有以下几种方法:
- Copying GC:将存活对象复制到另外的内存空间,以便腾出一块连续的空闲内存
- Mark-sweep GC:将死亡对象的内存标记为“可分配”(free),可以将需要内存的对象分配到free上
- Mark-compact GC:移动并整理存活对象,将存活对象移动到一起,使得可以后面内存分配时可以紧接着前面的已分配内存地址。
分代GC
每个对象都可以根据其经历过的GC次数来划定其年龄,针对年轻和老年的对象指定不太的GC策略,以降低整体内存管理的开销。
- 年轻代:由于年轻代中存活对象很少,可以采用copying GC
- 老年代:对象趋于一直存活,反复进行复制开销较大,因此可以采用mark-sweep GC
引用计数
让每个对象都有一个与之关联的引用数目,对象存活的的条件使:当且仅当引用数大于0时
优点:内存管理的操作被平摊到程序执行的过程中(在新建或销毁对象时对应的引用数就会修改),并且内存管理不需要了解 runtime 的实现细节。
缺点:由于要维护每个对象的引用计数,所以维护开销较大,因为需要原子操作来保证对计数操作的原子性和可见性。另外也无法回收环形的数据结构。