ART虚拟机本质上是将Android应用的执行策略从“运行时解释”转向“安装时预编译”的运行时环境,它把DEX字节码直接翻译成机器码,让应用启动更快、内存管理更高效。
聊ART之前,先得说清楚它取代的Dalvik虚拟机,Dalvik时代,应用每次启动时都通过JIT把热点代码编译成机器码,运行速度总是受编译时机拖累。行业共识认为,Dalvik的最大瓶颈在于每次冷启动都要重新解释、重新优化,白白消耗大量CPU时间,ART改变了这个节奏。
- Dalvik采用JIT编译,边运行边编译,启动慢但安装快
- ART早期采用AOT,安装时编译好,运行时直接执行机器码
- Android 7.0以后,ART走向“解释器+JIT+AOT”混合模式
从用户体感看,ART上线后差别很明显:同样一部中端手机,升级ART后应用冷启动时间普遍缩短,系统卡顿频率下降,代价是安装时间变长,包体占用空间变大,这个取舍在Android早期版本里相当明显,所以Google后续才补充了混合编译来平衡。
安装阶段的性能代价与回报
如果你用过Android 4.4或更老设备,安装大型游戏时等待的“正在优化应用”就是ART在跑dex2oat,这个阶段的CPU占用和耗电量会明显上升,部分测评显示老设备在安装大型应用时可能需要多等待一段时间,但换来的是每次打开应用的流畅感。
Android 7.0之后,ART在设备空闲且充电时做完整AOT编译,日常使用靠JIT临时加速,这样既保住了安装速度,也没有牺牲运行性能。
art虚拟机原理是什么?从dex2oat到混合编译
想真正理解ART的底层机制,核心先看dex2oat,这个名字是“dex to OAT”的意思,OAT文件是ART的可执行格式,本质上是包裹着机器码的ELF文件。
dex2oat的编译流水线大致如下:
- 解析DEX文件里的类定义和方法字节码
- 运行优化器做内联、常量折叠等优化
- 生成对应指令集的机器码,放进.oat段
- 保留一个紧凑的DEX副本供解释模式使用
整个链路里,优化器的决策直接影响最终性能,比如同样的循环体,开启内联后能减少函数调用开销,运行耗时下降幅度相当可观。
混合编译模式下的执行策略
从Android 7.0开始,ART内部有三套执行引擎在轮换使用:
- 解释器:最先启动,处理冷启动时的低频方法,不做任何编译
- JIT编译器:监测热点方法,达到阈值后编译并缓存到JIT代码缓存区
- AOT编译器:后台把频繁使用的方法完整编译成OAT机器码
这套机制很像一个学生先速读,发现常考知识点后精读,最后把笔记整理成一本小抄,三种模式之间的切换由Profile引导编译驱动,系统记录每次运行的采样数据,生成Profile文件,下次安装或更新时只编译高频方法。
art虚拟机垃圾回收机制详解:内存管理的底层逻辑
垃圾回收是ART最复杂的部分,相比Dalvik相对简单的回收器,ART引入了多个收集器,按内存压力按需切换。
ART的GC大体分三个级别:
- Sticky GC:只回收上次GC后新分配的对象,暂停时间极短
- Partial GC:回收堆中除一部分常驻区域外的空间,兼顾暂停时间和回收效率
- Full GC:整堆扫描回收,暂停时间最长,通常在内存压力极大时触发
多数情况下,ART优先触发Sticky GC,尽量避免Full GC带来的长暂停,配合并发标记算法,部分GC阶段的暂停时间可以控制在毫秒量级。
并发标记与暂停时间的博弈
ART的并发标记采用读屏障技术,让GC线程和应用线程可以同时运行,这意味着标记阶段不会长时间冻结应用,只有最后的清理阶段需要短暂暂停。
业内专家指出,Android 8.0以后ART用并发复制GC(Concurrent Copying GC)替代了CMS,配合读屏障把暂停时间压缩得更低,从玩家场景看,玩原神这类大型游戏时如果掉帧明显,可以在Logcat里找“Background young concurrent copying GC freed”这类日志,频繁出现并伴随较长耗时,往往意味着应用在短时间内创建了大量对象。
art虚拟机内存管理:对象分配与空间布局
说完GC,再看对象从哪来,ART的Java堆分成多个区域,各有分工。
- Main Space:常规对象分配区域,默认使用RosAlloc分配器
- Large Object Space:专门放大尺寸数组对象,比如位图解码时产生的整块像素数组
- Non-Moving Space:放置不可移动的对象,比如类对象和常量池
RosAlloc是ART专门优化的分配器,它把堆整理成多个尺寸接近的内存块,每次分配时根据对象大小匹配一个空闲块,这样做的好处是碎片少、分配快,代价是元数据占用稍大。
对象头非常关键,ART对象头里有一个32位的lock word,同时承担锁状态、GC标记和身份哈希码的存储,synchronized方法在ART里走轻量锁优化路径,锁竞争不激烈时通过原子CAS完成加锁,不会挂起线程。
从原理到实践:ART相关的性能排查方向
理解了底层机制,最终要落在解决实际问题上,开发者在线上遇到的ART性能瓶颈一般有这几个场景:
- 冷启动慢:先确认Profile引导编译是否生效,检查
/data/misc/profiles/cur下是否有对应包名的Profile文件 - 内存碎片化:大量小对象频繁创建会让RosAlloc碎片增加,观察Full GC频率是否异常
- 安装耗时过长:Gradle构建时开启
android.experimental.useR8和android.builder.enableDexingArtifactTransform=true,把部分优化前置到构建阶段
实操时,先在Android Studio里打开Memory Profiler,看GC类型和分配速率,再用systrace抓取GC暂停期间的帧渲染信息,这个组合可以覆盖大多数ART性能排查需求。
ART用“预编译+混合模式+精细化GC”换来了Android运行时的整体流畅,理解它的工作原理等于拿到一张系统排障地图,这套内存与编译机制仍在向低暂停、高并发的方向继续演进。
art虚拟机原理是什么?常见疑问解答
Q:art虚拟机与dalvik区别具体在哪?
A:核心区别在编译时机和编译策略,Dalvik是运行时JIT编译,ART是安装时或空闲时AOT编译,把字节码预先变成机器码,Android 7.0后的ART同时兼容两种策略,用Profile引导选择最优执行路径。
Q:ART为什么比Dalvik省电?
A:省电来自执行效率,运行机器码比解释字节码占用更少的CPU周期,同时GC暂停缩短也减少了系统唤醒频率,据Google公开的技术分享,ART在这方面的收益来自编译器优化和多代GC配合,而不是单纯的运行时参数调整。
Q:root后修改ART参数会影响系统稳定性吗?
A:会,修改堆大小上限、GC并发线程数这类参数,短期可能看到跑分提升,但多数情况下会导致系统进程被频繁杀死或触发异常GC,ROM调试阶段可以尝试,生产环境均使用ART默认配置。