Dalvik 与 ART 在垃圾回收机制上的主要区别有哪些?
解读
国内面试中,这道题既考察“历史包袱”又考察“演进思路”。
Dalvik 是 4.4 之前唯一的运行环境,ART 从 5.0 起全面替换,二者 GC 策略的差异直接决定了 App 卡顿、掉帧、OOM 的表象,因此面试官想听的是:
- 你能否把“Stop-the-World 时间”说透;
 - 你能否把“并发、并行、增量”三个词用在正确的上下文中;
 - 你能否把“内存碎片”与“GC 吞吐量”这对矛盾讲出工程取舍;
 - 你能否把“Native 层实现”与“上层表现”串成闭环。
答出这四点,基本就能拿到“深入理解”的评分。 
知识点
- 运行时架构:Dalvik 纯解释 + JIT,每次启动都重新编译;ART 采用 AOT(7.0 以前全量、7.0 以后混合 AOT/JIT+Profile Guided)。
 - GC 算法:
Dalvik:标记-清除(Mark-Sweep)两条线程,非并发,STW 时间长,内存碎片严重。
ART:
‑ 5.x 引入 CMS(Concurrent Mark-Sweep)+ 并行 GC,减少 STW;
‑ 8.x 起默认 Generational GC,分代 + 局部拷贝,进一步压缩碎片;
‑ 10+ 引入 Region-based GC(类似 G1),可预测停顿,支持 Humongous 对象快速回收。 - STW 粒度:Dalvik 全程挂起所有 mutator;ART 把标记、清理、引用处理拆成多次短暂停,单次停顿 <5 ms 是及格线。
 - 内存布局:Dalvik 堆连续,无压缩;ART 在 Generational 阶段引入 Bump-pointer 分配器 + RosAlloc,降低碎片 30% 以上。
 - 回收触发:Dalvik 仅按“固定阈值”触发;ART 引入“吞吐量预测 + 堆利用率预测”双因子,后台回收更积极,前台回收更克制。
 - 调试接口:Dalvik 仅 logcat 打印 GC_CONCURRENT;ART 提供 Trace::Begin/End 标记,可直接在 systrace 看到 GC 阶段耗时,方便定位掉帧根因。
 
答案
- 回收算法:Dalvik 采用单线程标记-清除,全程 Stop-the-World,一次 GC 可达 100 ms+;ART 使用并发标记 + 并行清理,把“标记”“清理”“引用入队”拆成多次短暂停,单次停顿控制在 5 ms 以内,基本消除卡顿。
 - 内存碎片:Dalvik 清除后产生大量空洞,触发更多 GC;ART 在 8.0 后引入分代 + 区域拷贝,对新生代使用 Bump-pointer 分配,老年代支持局部压缩,碎片率下降 30% 以上,降低 OOM 概率。
 - 吞吐量与后台策略:Dalvik 只在分配失败时触发,前台后台一视同仁;ART 根据应用前后台状态动态调整阈值,后台提前回收,前台尽量延迟,把 CPU 让给 UI 线程。
 - 大对象处理:Dalvik 大对象直接进老年代,无特殊优化;ART 把 >12 KB 对象放入单独 Region,回收时跳过小对象扫描,缩短标记时间。
 - 调试与可观测性:Dalvik 仅输出 GC_CONCURRENT/EXPLICIT 日志;ART 在 systrace 中透出 GC 阶段耗时,可直接定位“GC 导致掉帧”这一帧率杀手。
 
一句话总结:Dalvik 的 GC 是“单线程、非并发、碎片多、停顿长”,ART 演进为“并发标记 + 并行清理 + 分代压缩 + 可预测停顿”,把 16 ms 帧率红线从 GC 侧基本守住。
拓展思考
- 国内 ROM 定制常把 ART 的“后台 GC 线程数”从默认 2 改成 1 以省电,面试时可反问面试官“贵司 ROM 是否调整了并发度”,体现你对厂商魔改的关注。
 - 大型 App 在老机型上仍可能触发 long GC,可结合“GC 阈值调优”与“对象池复用”双管齐下:通过 
dalvik.vm.heapgrowthlimit放大堆增长斜率,同时用 RecyclerView 缓存池 + ArrayMap 替代 HashMap,减少临时对象。 - 未来 ART 继续往“Region-based + Pauseless”演进,面试尾声可以抛一句“听说 Android 14 的 GC 已经实验性引入染色指针,类似 ZGC,您团队有实测数据吗?”既展示技术敏感度,又把话题抛回给面试官,形成技术对等交流。