Android虚拟机与Java的协同运行,核心答案是:Java源码编译为class文件后,Android工具链会将其转为dex字节码,由Android虚拟机(ART)负责解释执行或编译为机器码,而Java语言自身的特性(面向对象、自动内存管理)则通过虚拟机的运行时组件来完成落地。这套机制让Android应用能跨设备运行,同时保证性能和资源可控。
从Java源码到安卓应用的旅程
Java代码并非直接被Android系统读取,Android环境中的Java,其实经过了两次"翻译"。
第一站,JDK编译器(javac)会把.java文件编译成.class文件,里面是JVM认识的字节码,但Android无法直接执行标准JVM字节码,于是下一站交给Android SDK中的d8工具(旧版是dx工具),它把.class文件合并重排,转成单个.dex文件,dex格式的诞生,是为了适配移动端的内存和CPU限制,它把多个class文件的冗余常量合并,体积更小。
打包进APK后,应用安装到手机,此时Android虚拟机(5.0+是ART,5.0前是Dalvik)正式接管,开始对dex字节码进行"二次加工"。
ART虚拟机的双引擎策略:JIT与AOT
ART虚拟机的工作方式,业界常形容为"先快后稳",它内部有两套执行引擎,协同完成Java代码的运行。
JIT编译:让应用快速启动
Android 7.0引入JIT(Just-In-Time)机制,应用首次启动或运行热点代码时,ART会启动即时编译,把频繁执行的dex字节码翻译成机器码,JIT的优点是启动快,只编译用到的代码,不浪费编译时间,但它也有代价,就是每次运行都要现场翻译,CPU开销存在。
一个电商应用启动时,ART会先JIT编译首页加载的核心路径,让用户快速看到界面,JIT编译的代码,会在特定条件下被记录到profile文件中。
AOT编译与dex2oat的实际应用
AOT(Ahead-Of-Time)发生在应用安装或系统空闲时。dex2oat(dalvik exec to optimized art)进程会把dex字节码预先编译成OAT文件(ART的私有格式),本质是机器码加dex源码的混合体。

系统执行AOT编译,会根据用户使用习惯和JIT生成的profile,优先编译高频代码块,这套"混合编译模式"(JIT+Profile+AOT)从Android 7.0开始成熟,到Android 9.0又加入了AOT编译的网络下载策略,减少了安装时间。
JIT与AOT的侧重点:
- JIT:内存占用高但启动快,适合冷启动阶段
- AOT:编译后执行快,适合高频调用路径
- 配合机制:后台空闲且充电时,ART会对已收集profile的应用执行AOT重编译
ART对Java内存管理的改造
Java程序员习惯new对象而不用管释放,这个"内存安全"承诺,在Android上由ART的垃圾回收器(GC)来实现,但移动端内存比服务器小得多,ART做了不少本地化调整。
Mark-Compact与Concurrent GC的协同
早期Dalvik使用Mark-Sweep算法,会产生大量内存碎片,ART则引入了Mark-Compact算法,在GC时把存活对象整理到连续内存区,减少碎片化,ART的并发GC(CMS)允许大部分标记阶段和应用线程并行执行,减少了卡顿。
从Android 8.0开始,ART还加入了并发复制(Concurrent Copying)GC,它能够在应用线程持续运行的情况下搬运对象,进一步削减GC停顿,据业内专家观点,这种GC设计借鉴了工业级JVM的经验,但针对移动端暂停时间做了深度定制。
为大对象堆开启的额外通道
Java里的Bitmap是内存大户,ART为Bitmap专门划出"图形堆"(Graphics Heap),不占用普通Java堆,但Bitmap的像素数据需要Native内存,这部分由C++代码管理,容易泄漏,因此开发者需要记得调用Bitmap.recycle()或依赖ByteBuffer机制来手动释放底层资源。
ART与标准JVM的五大差异
许多从后端转Android的开发者有困惑:JVM和ART到底哪里不一样?表里可以一图看清:
- 字节码格式:JVM读取class文件,ART读取dex文件,dex更紧凑
- 编译策略:JVM多用解释器+分层编译,ART默认开启AOT混编
- 内存模型:JVM运行在64位服务端,堆可配置大容量;ART受系统内存管理约束,堆上限由
dalvik.vm.heapsize决定 - 线程模型:JVM线程直接映射内核线程,ART线程调度同样基于Linux线程,但优先级更激进
- 调试接口:JVM使用JDWP协议,Android使用jdwp,但需要开启
ro.debuggable才能附加调试进程

这种差异带来的实际后果是:你不能把Java后端项目直接丢到Android上跑,需要适配Android API和资源管理环境。
Java虚拟机在安卓生态中的角色演变
随着时间推移,ART对Java的处理也在收敛。
从Dalvik到ART:解释器时代的终结
Android 5.0之前是Dalvik,纯解释执行,性能被iOS的Objective-C原生编译甩开,ART在Android 4.4上实验,5.0正式默认,彻底改变了Java代码的运行轨迹,一开始用全量AOT,安装时全编译,应用安装很慢,后来加入JIT和Profile引导编译,安装时间大幅回降。
对开发者意味着什么
Google官方文档指出,ART是Android运行时的主要实现,开发者无需关心JIT或AOT细节,只需写出高效的Java/Kotlin代码,但了解底层原理,能帮助你排查一些疑难问题。
一个应用在低配置手机上卡顿,可能是AOT编译没有覆盖到用户常用路径,此时开发者可以在Google Play控制台看启动基础剖析报告,或者用adb shell dumpsys dalvikvm查看当前运行模式和GC统计。
Java 17语言特性在Android的可得性
Android对Java语言版本的支持,依赖Android Gradle Plugin和工具链。AGP 8.x支持Java 17语法,但核心的desugaring(脱糖)机制,会把新语法转换为低版本API兼容的形式,开发中可以放心的用record、sealed class等写法,但最终执行仍是ART能理解的字节码形式。
ART从本质上讲,是一个为了续航和内存而优化的Java虚拟机

,它和Java的协同,既保留了跨平台、面向对象、自动内存管理的语言优势,又通过JIT/AOT混合编译和特定GC算法,适配了移动端特有的资源桎梏,对开发者而言,理解这套协同机制,意味着你能在写出可靠代码的同时,准确判断哪些性能问题瓶颈出现在Java层、哪些出现在虚拟机层,从而找到真正的优化路径。
Q&A:Android虚拟机与Java协同运行的常见疑问
Android虚拟机如何工作?
Android虚拟机指ART,它负责运行apk中的dex字节码,安装时dex2oat可能把部分代码预编译为OAT文件,运行时JIT对剩余热点代码即时编译,ART在Java内存模型之上,实现了对垃圾回收、线程同步和异常处理的运行时支持,是Java程序能跑在手机上的直接原因。
为什么Android用Java代码而不是直接用C++语言每次获取更好的性能?
因为Java提供跨平台运行时的便利,开发者只需一次编码,在各种硬件架构(ARM、x86、RISC-V)上都能运行,ART通过AOT把Java代码编译成本地机器码,已经把性能差距缩小到较小范围,只有图形渲染等高频路场景才会看到明显差异,多数业务场景下,Java的代码可维护性和生态资源,远大于底层语言带来的边际性能收益。
小米手机与三星手机上的Android虚拟机有什么区别?
不同厂商几乎不对ART本身做改造,系统层差异更多在于相册、游戏助手等Framework服务的实现,但厂商可以在系统镜像中调整ART参数:
- 调整堆内存上限
- 调整后台应用GC策略
- 调整AOT编译触发时间(例如充电时统一编译)
这些调整会导致同一APK在两台设备上出现运行微观差异,但开发者的Java代码无需修改,设备兼容性由兼容性定义文档(CDD)保证,厂商不能改变虚拟机行为的基础契约。
ART和Java的配合并非一成不变,它随Android版本迭代持续优化,开发者保持对新运行时行为的好奇,是解决疑难性能问题的最佳后盾。