【青训营】GC的两种常见技术| 青训营笔记

136 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第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的实现细节。

缺点:

  • 维护引用计数开销较大;通过原子操作保证对引用计数操作的原子性和可见性。
  • 无法回收环形数据结构。
  • 内存开销较大,每个对象都需要额外的内存空间储存引用数目。
  • 内存回收时仍可能引发暂停。