这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记.现代高级编程语言管理内存的方式分自动和手动两种。手动管理内存的代表是C和C++.编写代码过程中有程序员控制申请或者释放内存(但C++的智能指针可以实现自动内存管理);Java和Go等语言则使用自动内存管理系统. 本文主要讲述自动内存管理的两种最常见的技术。分别是追踪垃圾回收和引用计数。
自动内存管理指标
一般评价自动内存管理的指标有4个:
- 安全性(Safety):不能去回收存活的对象,这是基本要求
- 吞吐率(Throughput):公式为 1-(GC时间/执行总时间)
- 暂停时间(Pause time) :业务是否感知
- 内存开销(Space overhead): GC元数据开销
追踪垃圾回收
这个方法分为三步:
- 标记根对象:把静态变量,全局变量,常量,线程栈等标记出来。这是程序之后可能还要用到的对象,标记为存活。
- 标记可达对象:从根对象出发找到所有可达对象并标记为存活。
- 清理不可达对象:根据对象的生命周期(分代GC)选择不同的清理策略。
清理策略
主要有3种:
- Copying GC:将存活的对象复制到另外的内存空间。
- Mark-sweep GC:将死亡对象的内存标“可分配”。
- Mark-compact GC:在原内存空间移动并整理存活对象。
分代GC
分代GC认为每个对象都有自己的年龄,即经历过GC的次数,因此将对象分为年轻代(Young generation)和老年代(Old generation),对年轻对象和老年对象指定不同的GC策略,降低整体内存的开销.
- 年轻代(Young generation):存活对象少,CG吞吐率高,多采用Copying GC
- 老年代(Old generation):对象一直存活,不适合反复复制,采用Mark-sweep GC,避免多次内存分配。
引用计数
给予每个对象一个关联的引用计数,这是对该对象的活跃引用的数量。当对象的引用计数大于0时该对象存活,这个方法可以将内存管理的操作平摊到程序的执行中,C++的智能指针也是通过这种方式去实现。
优点:
- 内存管理的操作平摊到程序的执行。
- 不需要关注runtime的实现细节。
缺点:
- 维护引用计数开销较大;通过原子操作保证对引用计数操作的原子性和可见性。
- 无法回收环形数据结构。
- 内存开销较大,每个对象都需要额外的内存空间储存引用数目。
- 内存回收时仍可能引发暂停。