一、JVM垃圾回收机制(桌面/服务器端)
1. 核心算法:分代收集
新生代回收(Minor GC)
触发条件:Eden区满时触发
算法:复制算法(Eden → Survivor区)
过程:存活对象在Survivor区间复制,年龄+1;年龄超阈值(默认15)晋升老年代
老年代回收(Major GC/Full GC)
触发条件:老年代空间不足
算法:标记-清除(产生碎片)或标记-整理(无碎片)
耗时:10倍于Minor GC,导致应用暂停(STW)
2. 对象存活判定:可达性分析
GC Roots类型:
虚拟机栈局部变量
方法区静态变量与常量
JNI引用对象
解决循环引用:不可达对象判定为垃圾(对比引用计数法)
3. GC触发场景
GC类型 | 触发条件 | 影响范围 |
---|---|---|
Minor GC | Eden区满 | 仅新生代 |
Full GC | 老年代满/调用System.gc() | 全堆+方法区 |
MetaSpace GC | 类元数据超限 | 元空间 |
二、Dalvik垃圾回收机制(Android 4.4及之前)
1. 核心设计:移动端适配
堆结构:
Zygote堆:预加载系统类(进程间共享)
Active堆:应用独享,对象分配主区域
回收算法:标记-清除(Mark-Sweep)
位图标记:独立空间记录对象状态,减少对象头开销
三次STW:
每次暂停约5-10ms,导致界面卡顿
2. 致命缺陷
全堆扫描:每次GC需遍历所有对象
内存碎片:清除后产生不连续空间,大对象分配失败
高功耗:频繁GC增加CPU负载
三、ART垃圾回收机制(Android 5.0+)
1. 革命性优化
2. 核心机制解析
并发标记清除(CMS):
标记阶段:
初始标记(STW暂停1次):标记根对象(耗时≤1ms)
并发标记:与应用线程并行遍历引用链
最终标记(非STW):处理引用变更(ModUnionTable记录脏数据)
清除阶段:后台线程异步回收
分代策略增强:
年轻代:复制算法(Minor GC <2ms)
老年代:标记-整理(避免碎片)
大对象直存老年代:避免年轻代频繁回收
3. GC触发条件
GC原因 | 触发场景 | 线程影响 |
---|---|---|
kGcCauseForAlloc | 分配对象时内存不足 | STW暂停 |
kGcCauseBackground | 后台并发GC(堆使用达阈值) | 无STW |
kGcCauseExplicit | 调用System.gc() | STW暂停 |
四、三大运行时GC机制对比
维度 | JVM | Dalvik | ART |
---|---|---|---|
堆结构 | 新生代+老年代+元空间 | Zygote堆+Active堆 | Image/Zygote/Allocation/Large Object Space |
回收算法 | 分代收集(复制+标记整理) | 标记-清除(全堆扫描) | CMS(并发标记+增量清除) |
STW暂停 | Full GC时显著暂停 | 3次暂停/次GC | 仅1次初始标记暂停 |
碎片处理 | 标记整理压缩 | 无优化(依赖Bionic) | 在线内存压缩(ART 10+) |
移动端优化 | 无 | 写时复制共享Zygote堆 | AOT+JIT混合编译 |
典型GC耗时 | Full GC:100ms+ | 每次暂停5-10ms | Minor GC:<2ms;Full GC:5-10ms |
五、面试标准答案(背诵版)
Q:JVM、Dalvik与ART的GC核心区别?
A: 三者本质是不同场景的运行时环境,核心差异如下:
算法设计:
JVM:分代收集(新生代复制算法+老年代标记整理)
Dalvik:标记-清除(全堆扫描,三次STW卡顿严重)
ART:并发标记清除(CMS仅1次STW,增量清除减少卡顿)
堆结构:
JVM:新生代(Eden+Survivor)+老年代
Dalvik:Zygote堆(共享)+Active堆(进程独享)
ART:四空间划分(Image预加载类+Large Object专存大对象)
移动端优化:
Dalvik:写时复制共享系统类(节省内存)
ART:AOT预编译减少运行时开销,并发GC降低STW至1次
性能指标:
卡顿:ART(5ms)< Dalvik(30ms)< JVM Full GC(100ms+)
内存利用率:ART > JVM > Dalvik(碎片问题)128
Q:ART如何实现高效GC?
A: 四大关键技术:
并发标记:通过ModUnionTable记录引用变更,标记阶段仅需1次STW
增量清除:回收过程与应用线程并行
堆分区:Large Object Space隔离大对象,减少年轻代压力
AOT预编译:安装时生成机器码,减少运行时解释开销