Dalvik:它是什么、虚拟机以及它是如何工作的

作者: IT Sectr 发布日期: 2026-04-16 阅读时间: 9 分钟

虚拟机 Dalvik 是 Android 操作系统的关键组件,负责执行应用程序直到 4.4 KitKat 版本。由丹·伯恩斯坦开发的基于寄存器的虚拟机取代了标准 JVM 的概念,使得可以针对内存有限的移动设备优化应用程序的启动。根据 Google, 2024 的数据,Dalvik 通过 JIT 编译确保应用程序兼容性,在运行时直接将 DEX 字节码转换为机器指令。

要点

  • Dalvik 是采用寄存器架构的虚拟机,专为 Android 优化。
  • JVM 不同,Dalvik 执行专门为移动设备压缩的 DEX 字节码。
  • JIT 编译 在应用程序运行期间直接将部分 DEX 代码转换为机器代码。
  • 从 Android 5.0 开始,Dalvik 被采用预先 AOT 编译的 ART 取代。
  • 理解 Dalvik 对于支持旧版 Android 和分析向后兼容性非常必要。

什么是 Dalvik?

Dalvik 是采用寄存器架构的虚拟机,专门为 Android 平台创建。开发始于 2005 年,由丹·伯恩斯坦的公司进行,2007 年该项目被 Google 收购。第一个商业版本的 Dalvik 随着 2008 年 Android 1.0 的发布而出现。

与标准 Java Virtual Machine(JVM)不同,Dalvik 不执行 Java 字节码。Java 编译器将源代码转换为 class 文件,然后 dx 工具将它们转换为 Dalvik Executable(DEX)格式。这种格式比 class 文件更紧凑:一个 10 MB 的 class 格式应用程序在 DEX 中约占 6–7 MB。

创建历史

丹·伯恩斯坦将 Dalvik 编写为面向资源有限的操作系统的项目。这个名字取自冰岛村庄达尔维克。Google 之所以选择 Dalvik 而不是 JVM,是因为许可限制以及需要针对带有 ARM 架构的移动处理器进行深度优化。该系统迅速流行起来:到 2012 年,超过 5 亿台 Android 设备运行在 Dalvik 上。

在 Android 生态系统中的角色

每个 Android 应用程序都在单独的进程中运行,拥有自己的 Dalvik VM 实例。这确保了操作系统层面的数据隔离和恶意代码防护。这种方法将虚拟化的优势与 Linux 沙箱相结合——一个应用程序中的恶意软件无法影响相邻的进程。

Dalvik 架构:寄存器机器和 DEX

Dalvik 的 寄存器架构从根本上不同于 JVM 的堆栈架构。Dalvik 不是在栈顶进行操作,而是操作寄存器——虚拟机内部的虚拟单元。每条指令包含操作数寄存器的地址,这减少了每次操作的指令数量。

JVM 的堆栈机器使用 push、pop 和 add 等指令——将两个数字相加需要三条指令。Dalvik 用一条带三个寄存器的 add-int 指令解决同样的任务。根据 Android Open Source Project 的数据,DEX 的寄存器架构平均将字节码体积比堆栈式 class 格式减少 30%。

DEX 格式

DEX 文件(Dalvik Executable)包含应用程序所有类的压缩表示。文件头包括校验和、各节大小和偏移量。主要部分是字符串池、类型池、方法原型池、字段池以及字节码本身。一个 DEX 文件中最多可以存储 65536 个方法(该限制通过 Android 5.0 中引入的 multi-dex 解除)。

要将 class 文件转换为 DEX,需要使用 dx 工具,它包含在 Android SDK Build Tools 中。命令示例:dx --dex --output=classes.dex myapp.jar。现代项目使用 D8——dx 的继任者,具有改进的优化和对 Java 8+ 功能的支持。

bash
# 使用 dx 将 JAR 转换为 DEX
dx --dex --output=classes.dex myapp.jar

# 通过 D8 的现代版本
d8 --lib android.jar --output dex/ myapp.jar

Zygote:框架预加载

Zygote 进程是 Dalvik 架构中最重要的元素。系统启动时,Zygote 加载 Android SDK 的所有类,打开共享库并创建预加载资源池。当用户打开应用程序时,系统复制(fork)Zygote 进程,创建已经拥有现成框架的 Dalvik VM 新实例。这将应用程序启动时间从约 2–3 秒缩短到 300–500 毫秒。

Dalvik 中的 JIT 编译

JIT(Just-In-Time) 是一种在应用程序运行期间直接将字节码编译为机器指令的技术。在 Dalvik 中,JIT 编译器分析正在执行的 DEX 代码,识别经常使用(hot)的方法,并将它们编译为 CPU 的原生代码。

在早期 Android 版本中选择 JIT 而不是完整的 Ahead-Of-Time(AOT)编译是有意为之。移动设备的 闪存有限(4–16 GB)——预先编译所有应用程序会占用大量空间。此外,早期设备中的 ROM 存储器比 RAM 慢,读取预编译代码可能会降低性能。

JIT 编译过程

当应用程序启动时,Dalvik 开始解释 DEX 字节码。一个特殊的分析器跟踪哪些方法被最频繁调用。超过阈值(通常约 200 次调用)后,JIT 编译器将方法转换为机器代码并缓存在内存中。后续调用使用已编译的版本,无需重新编译。

java
// JIT 将编译的 hot 方法示例
public class Calculator {
    public int sumArray(int[] arr) {
        int total = 0;
        for (int i = 0; i < arr.length; i++) {
            total += arr[i];
        }
        return total;
    }
}

JIT 的性能

根据 Google I/O 2013 的数据,在 Android 2.2 Froyo 中引入 JIT 使应用程序执行平均比纯解释快 2–5 倍。然而,JIT 在首次启动时会增加延迟:应用程序需要 3 到 10 秒来预热和编译 hot 方法。预热后,性能稳定在接近原生代码的水平。

Dalvik 与 JVM:关键区别

Dalvik 与 JVM 在几个基本参数上有所不同。第一是架构:JVM 基于堆栈,Dalvik 基于寄存器。第二是字节码格式:JVM 使用 class 文件,Dalvik 使用 DEX。第三是内存管理:Dalvik 针对移动设备的 有限内存进行了优化。

两种方法都有各自的优势。基于堆栈的 JVM 存储指令占用的空间更少——每条指令更短,因为操作数隐式取自堆栈。基于寄存器的 Dalvik 每次操作执行的指令更少,这节省了处理器时间并降低了功耗。对于使用电池供电的移动设备来说,这至关重要。

参数DalvikJVM
架构寄存器堆栈
字节码DEXclass
编译JIT(Android 2.2+)JIT / AOT
优化低功耗高兼容性
隔离通过 Linux 进程通过 ClassLoader

许可方面

选择 Dalvik 而不是 JVM 也是由 许可决定的。Oracle 拥有 Java SE 和 JVM 的权利,Google 力求避免支付许可费。创建具有替代字节码格式的自有 VM 使 Android 能够独立于 Oracle 发展。这一争端演变成 Oracle 与 Google 之间长达多年的诉讼(2010–2021),最终以 Google 胜诉告终。

DEX 格式和 dx 工具

DEX(Dalvik Executable) 是包含 Android 应用程序编译代码的二进制格式。每个 DEX 文件都以头(header)开始,后面是各节:字符串常量(string_ids)、类型(type_ids)、方法原型(proto_ids)、字段(field_ids)、方法(method_ids)、类定义(class_defs)和数据区域(data)。

dx 工具将 Java class 文件转换为一个或多个 DEX 文件。工作算法包括常量去重——相同的字符串或类型只保存一次并按索引引用。这显著减小了最终体积。在现代项目中,dx 已被 D8 取代(出现在 Android Studio 3.1 中),它运行快 2–3 倍并支持 Java 8 desugaring。

java
// 通过 dexdump 反编译 DEX 字节码的示例
// 源代码:return a + b;
@Ldalvik/annotation/Code;
    registers: 3
    add-int v0, v1, v2
    return v0

Multi-dex:突破 65536 限制

DEX 格式对 65536 个方法的限制(16 位索引的限制)成为大型应用程序的严重问题。解决方案出现在 Android 5.0:multi-dex 支持允许应用程序包含多个 DEX 文件。主要的 classes.dex 包含入口点,而额外的 classes2.dex、classes3.dex 等等包含其余代码。multi-dex 配置通过 build.gradle 中的 multiDexEnabled true 行启用。

内存管理和垃圾回收

Dalvik 中的垃圾回收实现为带标记清除(mark-and-sweep)的分代(generational)收集器。内存分为两个主要区域:用于对象的 Heap(堆)和用于基本类型与引用的 Stack(栈)。当 Heap 填满时,Dalvik 暂停所有线程(STW — Stop-The-World),标记可达对象并释放不可达对象。

在 Android 2.2 之前,Dalvik 使用单线程收集器,暂停时间长达 100–200 毫秒。在 Android 2.3 Gingerbread 中出现了并发收集器,将典型暂停缩短到 5–10 毫秒。而在 Android 4.0 Ice Cream Sandwich 中添加了具有部分(增量)清理的收集器——Concurrent Mark and Sweep(CMS)。

内存泄漏

Dalvik 应用程序的典型问题是通过对 Activity 的静态引用造成的内存泄漏。如果静态字段保存对 Context 或 View 的引用,垃圾收集器即使在屏幕关闭后也无法释放 Activity。Eclipse MATLeakCanary 等工具有助于发现此类泄漏:它们分析 Heap 转储并显示持有对象的引用链。

java
// 通过静态引用造成内存泄漏的示例
public class Utils {
    private static Context context;

    public static void init(Context ctx) {
        context = ctx; // 在 finish() 之后仍持有 Activity
    }
}

Dalvik 的局限性和向 ART 的过渡

尽管取得了成功,Dalvik 仍有一系列缺点。JIT 编译需要预热时间——应用程序运行的最初几秒较慢。此外,JIT 在编译时会消耗处理器电量,缩短了续航时间。随着移动设备性能的提高和内置内存容量的增加,对 JIT 的需求降低了。

在 Android 4.4 KitKat 中,Google 将 ART(Android Runtime)作为 Dalvik 的实验性替代品推出。从 Android 5.0 Lollipop 开始,ART 成为唯一的运行时环境。主要区别是 AOT 编译:不是在运行时编译,而是在安装时将所有应用程序编译为机器代码。这消除了预热延迟并改善了能效

向后兼容性

从 Dalvik 到 ART 的过渡对开发人员是透明的:两种运行时都执行相同的 DEX 字节码。为 Dalvik 构建的应用程序可以在 ART 上无需重新编译即可运行——system_server 在安装时将它们编译为原生代码。例外情况是使用反射访问 Dalvik VM 内部成员的代码:由于内部架构的变化,此类代码可能在 ART 上出错。

常见问题

简单地说,什么是 Dalvik?

Dalvik 是一个中间程序,在手机上运行 Android 应用程序。它获取应用程序的代码并将其转换为处理器可以理解的指令,在用户使用过程中直接完成。

Dalvik 与 JVM 有什么区别?

Dalvik 使用寄存器架构和 DEX 格式,而 JVM 使用堆栈架构和 class 格式。Dalvik 针对内存和处理器有限的移动设备进行了优化,而 JVM 面向台式计算机和服务器设计。

为什么 Google 用 ART 取代了 Dalvik?

ART 通过预先 AOT 编译提供更高的性能——应用程序在安装时编译一次,而不是每次启动时编译。与 Dalvik 的 JIT 方法相比,这加快了运行速度并节省了电池电量。

旧应用程序能在 ART 上运行吗?

可以,ART 完全向后兼容 Dalvik 的 DEX 字节码。安装时,ART 将旧的 DEX 文件编译为原生代码。例外情况是使用反射访问 Dalvik 内部机制的应用程序。

什么是 DEX 文件?

DEX(Dalvik Executable)是包含 Android 应用程序压缩字节码的可执行文件格式。如果应用程序包含超过 65536 个方法,一个 APK 中可以有多个 DEX 文件(multi-dex)。

结论

  • Dalvik VM 是为 Android 创建并一直使用到 4.4 KitKat 版本的寄存器虚拟机。
  • DEX 格式确保字节码紧凑存储——比 JVM 的 class 文件少 30%。
  • Dalvik 中的JIT 编译使应用程序执行比纯解释快 2–5 倍。
  • Zygote 进程预加载 Android 框架,将应用程序启动缩短到 300–500 毫秒。
  • 单个 DEX 文件中 65536 个方法的限制从 Android 5.0 开始通过 multi-dex 解决。
  • Dalvik 中的垃圾回收经历了从单线程 STW 到 Concurrent Mark and Sweep 的演变。
  • Android 5.0 中向 ART 的过渡消除了 JIT 预热延迟并改善了能效。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读