一、ART概览:从Dalvik到ART的演进

说起Android Runtime,我脑子里第一个蹦出来的词就是「进化」。从2008年Android 1.0发布时的Dalvik虚拟机,到2014年Android 5.0全面转向ART,这中间的变化,说白了就是一场性能革命。

我记得刚入行那会儿,团队里还在用Dalvik。每次编译完APK,测试同事总会抱怨:「这应用启动怎么这么慢?」嗯,那时候我们只能苦笑——Dalvik的JIT编译模式,天生就有这个短板。

Dalvik时代的痛点

Dalvik虚拟机用的是JIT(Just-In-Time)编译。什么意思呢?应用每次启动时,Dalvik才会把热点代码编译成机器码。你想想看,每次打开应用都要现编译,启动速度能快才怪。

我在项目中遇到过最典型的场景:一个电商应用,首页加载要等3-4秒。用户早就不耐烦了。后来我们做了各种优化,但Dalvik的JIT模式决定了——你再怎么优化,启动速度的上限就在那儿。

核心问题:Dalvik的JIT编译是「边跑边编译」,每次启动都要重复这个过程。电量消耗大,CPU占用高,用户体验差。

ART的诞生

ART(Android Runtime)从Android 4.4开始作为实验性功能出现,到5.0正式取代Dalvik。它的核心变化就一个:把JIT换成了AOT(Ahead-Of-Time)预编译

说白了,就是在应用安装时,ART直接把DEX字节码编译成机器码。以后每次启动,直接执行机器码就行,不用再编译了。

我曾经在项目里做过对比测试:同一个应用,Dalvik环境下启动需要2.8秒,ART环境下只要0.9秒。这个差距,用户是能明显感知到的。

个人经验:ART的AOT编译虽然让安装时间变长了(因为安装时要编译),但换来的是日常使用的流畅体验。我建议在开发阶段用debug包,发布时用release包,这样既能保证开发效率,又能让用户享受AOT的好处。

二、ART核心设计目标

ART的设计目标,我总结为三个词:快、省、稳

1. 启动速度更快

这个不用多说,AOT预编译直接解决了Dalvik的启动慢问题。但ART不只是做了AOT这么简单。

ART还引入了Profile-Guided Optimization(PGO)。什么意思呢?系统会记录用户常用的应用和代码路径,然后针对这些热点代码做更激进的优化。不常用的代码,就保持解释执行或者用JIT编译。

我在做系统优化时发现,PGO对冷启动的优化效果特别明显。比如微信,第一次打开可能要1.2秒,但用了PGO之后,能降到0.6秒左右。

2. 内存占用更省

ART在内存管理上做了很多改进。比如紧凑的堆布局,减少了内存碎片。还有更高效的GC(垃圾回收),暂停时间从Dalvik的几十毫秒降到了几毫秒。

我记得有一次,团队在优化一个图片处理应用。Dalvik下GC频繁触发,导致界面卡顿。换成ART后,GC次数减少了40%,卡顿基本消失了。

关键数据:ART的GC暂停时间平均在2-5ms,而Dalvik通常在10-30ms。这个差距在动画场景下特别明显。

3. 系统更稳定

ART的稳定性提升,主要体现在异常处理类型安全上。ART在编译阶段就能发现很多类型错误,而不是等到运行时才崩溃。

我曾经踩过一个坑:Dalvik下,一个泛型类型转换错误,直到用户操作到某个特定页面才崩溃。换成ART后,安装时就直接报错了。虽然开发时觉得烦,但想想看——总比用户遇到闪退强吧?

避坑指南:我曾经在项目里遇到过ART编译时OOM的问题。原因是应用太大,AOT编译需要大量内存。解决方案是开启android:extractNativeLibs="true",或者使用Android 7.0引入的混合编译模式。

三、ART与JVM的对比

很多人问我:「ART和JVM到底有什么区别?」嗯,这个问题问得好。虽然两者都是运行时环境,但设计思路完全不同。

核心差异一览

对比维度 ART JVM(标准实现)
字节码格式 DEX(Dalvik Executable) Class/Java字节码
编译策略 AOT + JIT + 解释执行 JIT + 解释执行
内存模型 紧凑堆、低碎片 传统堆、碎片较多
GC策略 并发标记、低暂停 分代收集、暂停较长
多线程支持 基于Linux pthread 基于操作系统线程
资源限制 严格(移动设备) 宽松(服务器/桌面)

字节码格式的不同

JVM用的是标准的Java字节码,每个Class文件对应一个类。而ART用的是DEX格式,把所有类的字节码打包到一个文件里。

这样做的好处很明显:减少IO次数。你想想看,一个应用可能有几千个类,如果每个类一个文件,加载时得打开几千次文件。DEX格式一次加载就搞定了。

我在做性能分析时发现,DEX格式的加载速度比Class格式快3-5倍。这在移动设备上尤其重要——毕竟手机的IO性能比不上服务器。

编译策略的差异

JVM主要靠JIT编译,热点代码在运行时被编译成机器码。ART则更灵活:

  • 安装时:AOT编译,生成机器码
  • 运行时:JIT编译热点代码
  • 空闲时:后台重新编译优化

这种混合策略,说白了就是「既要又要还要」——既要启动快,又要运行快,还要省电。

我的建议:如果你是从JVM转过来做Android开发,记住一点——ART对内存更敏感。在JVM里你可能习惯new一大堆对象,但在ART里,对象创建和GC的开销更大。尽量复用对象,减少临时分配。

内存模型的差异

ART的内存模型比JVM更紧凑。它使用了TLAB(Thread-Local Allocation Buffer)紧凑堆技术,减少了内存碎片。

我记得在优化一个视频播放器时,Dalvik下内存碎片率高达15%,导致频繁触发GC。换成ART后,碎片率降到了3%以下,播放流畅多了。

四、知识体系总览

下面这张图,是我整理的ART核心知识体系。你可以把它当作学习路线图:

ART运行时 编译策略 AOT预编译(安装时) JIT即时编译(运行时) PGO热点优化 内存管理 紧凑堆布局 低暂停GC TLAB线程本地分配 字节码与执行 DEX字节码格式 解释执行 机器码直接执行 核心目标:启动快 + 运行快 + 省电 + 稳定 从Dalvik到ART:JIT → AOT+JIT混合

这张图展示了ART的三个核心模块:编译策略、内存管理、字节码与执行。它们共同支撑起了ART「快、省、稳」的设计目标。

五、总结

从Dalvik到ART的演进,本质上是Android系统对性能追求的体现。ART通过AOT预编译、紧凑内存管理和灵活的编译策略,解决了Dalvik时代的痛点。

我个人觉得,理解ART的关键在于把握它的设计哲学:在资源受限的移动设备上,用编译时的开销换取运行时的效率。这个思路,其实在很多系统优化中都能看到影子。

嗯,这一章的内容就到这里。下一章我们会深入ART的编译流程,看看DEX字节码到底是怎么变成机器码的。


公众号:蓝海资料掘金营,微信deep3321