ART的GC算法
核心演进历程
了解ART GC的演进历史非常重要:
- Dalvik (Android 4.4及之前):主要使用分代GC + Mark-Sweep。一个显著的特点是Stop-The-World (STW),即进行GC时,所有应用线程都会暂停,导致应用卡顿
- ART (Android 5.0 - 8.0):引入了多种GC方案,并大力优化以减少STW时间。这个阶段是并发标记和并发拷贝的成熟期
- ART (Android 8.0 - 10.0):在并发拷贝的基础上,引入了Generational Concurrent Copying (GCC),即分代并发拷贝,成为了默认的GC策略,对堆空间进行了明确的代际划分
- ART (Android 11+):对GCC进行了进一步优化,并开始为特定设备(通常是低内存设备)引入Region-based GC,其思想类似于JVM中的G1,但实现上更轻量
深入理解主要GC算法
ART的GC并非单一算法,而是一个组合策略,针对不同情况(如堆大小、碎片化程度、Android版本)会选择不同的方案。我们重点分析您提到的以及ART中最核心的几种
1)Concurrent Copying (CC) - 并发拷贝
这是ART现代GC的基石,尤其是在Android 8.0之后
- 核心思想:将堆分为两个空间(From-Space 和 To-Space)。GC开始时,应用线程短暂暂停(STW),进行根节点枚举。之后,应用线程恢复,GC线程并发地将所有从根节点可达的存活对象从From-Space拷贝到To-Space。拷贝完成后,再次短暂STW,切换一些状态,然后回收整个From-Space(里面全是垃圾)
- 优点:
○ 极高的吞吐量:回收内存就是直接“扔掉”整个From空间,速度极快
○ 避免碎片化:在拷贝过程中,存活对象被紧凑地排列在To-Space,自然地完成了内存整理
○ 大部分阶段并发:主要的拷贝工作是和应用线程并发进行的,极大地减少了STW时间 - 缺点:
○ 需要额外空间:需要有一半的堆空间作为“To-Space”备用,内存使用效率不是100%
○ 拷贝开销:如果存活对象非常多,拷贝过程会有一定开销
它与JVM中CMS/G1的关系:
CC更像是一种基础回收算法,而CMS/G1是更高级的管理器。CC是ART实现低延迟目标的关键技术,类似于G1和ZGC中使用的拷贝/转移技术
2)Generational Collection - 分代收集
这是一个管理策略,而非具体回收算法。从Android 8.0 (Oreo) 开始,ART的默认GC策略明确采用了分代模型,称为 Generational Concurrent Copying (GCC)
- 核心思想:基于“弱代假说”(Weak Generational Hypothesis)——绝大多数对象都是朝生夕死的
- 堆划分:
○ Young Generation (年轻代):存放新创建的对象。分为一个Eden区和两个Survivor区 (S0, S1)
○ Old Generation (老年代):存放经过多次GC仍然存活的对象 - GC类型:
○ Minor GC / Young Collection:只回收年轻代。频率非常高,但速度快,因为要处理的对象集很小
○ Major GC / Full Heap Collection:回收整个堆(年轻代+老年代)。频率低,但耗时长,STW风险更高 - 工作流程:
① 新对象分配在Eden区
② Eden区满时,触发一次Minor GC。使用并发拷贝算法,将Eden和当前活动的Survivor区中的存活对象拷贝到另一个Survivor区,年龄+1。拷贝几次后(年龄达到阈值),对象被提升(Promote)到老年代
③ 老年代空间不足时,触发Major GC
分代的目的:通过频繁、快速地回收年轻代,避免大量短期存活的对象被提升到老年代,从而减少耗时更长的Major GC的发生频率
3)CMS & G1 在ART中的对应
- CMS (Concurrent Mark-Sweep):
ART在早期版本(如Android 7.0)中有一个名为 Concurrent Mark-Sweep (CMS) 的回收器,主要用于老年代回收。它的目的是与应用程序并发执行,减少STW。但其核心是标记-清除(Mark-Sweep),不会移动对象,因此会导致内存碎片。在后续版本中,由于并发拷贝在大多数场景下表现更优(能整理碎片),CMS逐渐被弃用 - G1 (Garbage-First):
ART没有直接移植JVM的G1回收器。但是,从 Android 11 开始,ART为低内存设备(通常RAM<2GB)引入了一种新的 Region-based GC
○ 相似之处:它将堆划分为多个固定大小的Region,像G1一样。这允许GC优先收集那些包含最多垃圾的Region(Garbage-First思想的体现),从而更高效地回收内存,尤其是在堆很大但碎片化严重时
○ 不同之处:ART的实现比HotSpot JVM的G1更简单、更轻量级,主要是为了适应移动设备的资源约束。它可能不是所有设备的默认选项,而是作为一种针对低内存设备的优化方案
现代ART GC (以GCC为例) 的详细过程
一次完整的GC周期是多种技术的融合:
- STW: 暂停应用线程,枚举根对象 (GC Roots)
- 并发标记:恢复应用线程,GC线程遍历对象图,标记所有可达对象。应用线程同时在分配新对象,这些新对象被初始标记为“存活”并记录在写屏障(Write Barrier)的队列里
- 处理脏对象:再次STW(时间很短),重新扫描在并发标记阶段被修改的对象(通过写屏障记录),确保标记完整性
- 回收决策:根据标记结果和选择的GC策略(如是否分代),决定回收哪些空间。如果是年轻代回收,就回收Eden和一个Survivor区
- 并发拷贝/压缩:将决定要回收的区域中的存活对象拷贝到空闲区域(另一个Survivor区或老年代)。这个拷贝过程大部分是并发的
- 回收内存:拷贝完成后,原本的空间被全部回收,可以用于后续分配
总结与对比
特性/算法 | Concurrent Copying (CC) | Generational CC (GCC) | (ART historical) CMS | Region-based (Android 11+) |
---|---|---|---|---|
核心思想 | 拷贝存活对象,整区回收 | 分代假设 + 并发拷贝 | 并发标记,不清算 | 分区管理,优先回收垃圾多区域 |
碎片处理 | 有效避免(整理压缩) | 有效避免(整理压缩) | 产生碎片(标记清除) | 有效避免(可整理) |
STW时间 | 短(主要阶段并发) | 非常短(YGC快) | 较短(并发标记) | 目标为短 |
内存开销 | 较高(需一半堆做备份) | 中等 | 低 | 中等(元数据记录) |
适用场景 | 中高端设备,默认策略 | Android 8.0+ 默认策略 | ART旧版本老年代回收 | 低内存设备,解决碎片 |
结论:
现代ART的GC是一个以 分代并发拷贝 (GCC) 为主要支柱的高度优化的系统。它借鉴了而非直接照搬JVM中的CMS和G1
- 它用 Concurrent Copying 实现了类似ZGC/Shenandoah的低延迟目标
- 它用 Generational 设计来最大化回收效率
- 它在Android 11+中为特定硬件引入了 Region-based 的思想来应对G1所解决的类似问题(大堆、低碎片)