Android 虚拟机的奇妙工厂之旅:从 Dalvik 到 ART 的技术童话

一、工厂的诞生:Dalvik 与 Java 虚拟机的差异之谜

想象 Android 系统是一个大型电子设备工厂,每个应用都是工厂里的独立车间。Dalvik 虚拟机就是管理这些车间运转的 "智能调度系统",而它的 "表哥"Java 虚拟机则在另一个大型工厂(Java 生态)里工作。

1.1 不同的生产流水线:指令集的秘密

Java 虚拟机的生产线是 "基于堆栈" 的:就像用托盘传递零件,每次取放都需要先放到托盘(堆栈)上,再从托盘取走。对应的指令如loadstore就像 "把零件放到托盘" 和 "从托盘取零件",虽然灵活但步骤多。

java

arduino 复制代码
// Java字节码示例(基于堆栈)
iload_0  // 从堆栈加载第0个局部变量
iload_1  // 加载第1个局部变量
iadd     // 执行加法,结果放回堆栈

Dalvik 虚拟机的生产线是 "基于寄存器" 的:就像工人直接用手传递零件,寄存器就是工人的手,可以直接操作零件。指令长度更长(2-6 字节),但减少了来回搬运的步骤。

plaintext

arduino 复制代码
// Dalvik字节码示例(基于寄存器)
add-int v0, v1, v2  // 直接用寄存器v1+v2,结果存v0
invoke-virtual {v0}, Ljava/lang/String;.length:()I  // 调用方法

1.2 打包方式的智慧:Dex 文件的压缩魔法

Java 工厂的每个零件(类)单独打包成.class文件,就像每个零件用独立盒子装。而 Dalvik 工厂把多个零件(类)合并打包成.dex文件,就像把同类零件放进一个大箱子,重复的标签(字符串)只贴一次。

plaintext

scss 复制代码
// Dex文件生成流程
Java代码(.java) → javac → Java字节码(.class) 
→ dx工具 → Dalvik字节码(.dex) → dexopt优化 → 优化的Odex文件

二、工厂的启动:Zygote 母工厂的复制魔法

2.1 母工厂的初始化:Zygote 的启动流程

Android 系统启动时会先创建一个 "母工厂"Zygote,它预加载了所有应用共享的核心资源(如 Java 类库、系统资源)。当需要启动一个新应用(如微信)时,Zygote 会通过 "复制" 机制创建子工厂,就像用复印机快速克隆出车间。

c++

rust 复制代码
// Zygote启动核心代码(简化版)
void AndroidRuntime::start(const char* className, bool startSystemServer) {
    // 1. 启动虚拟机
    if (startVm(&mJavaVM, &env) != 0) return;
    // 2. 找到启动类(如ZygoteInit)
    jclass startClass = env->FindClass(slashClassName);
    // 3. 调用main方法启动
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
}

2.2 复制工厂的魔法:COW 机制的节能技巧

Zygote 复制出子工厂时,并不会立即复制所有资源,而是采用 "写时复制(COW)" 策略:就像多个车间先共享同一套蓝图,直到某个车间需要修改蓝图时才单独复制。这样大大减少了内存占用和启动时间。

plaintext

arduino 复制代码
// 内存共享示意图
Zygote进程 ←─┬─ 共享Java核心库
System Server进程 ←─┼─ 共享Android核心库
应用进程 ←─┴─ 共享系统资源

三、工厂的内存管理:仓库管理员的烦恼

3.1 三类仓库的分工

  • Java 对象仓库(Java Heap) :存放 Java 对象,大小有限制(如 16M-48M),由虚拟机自动管理。就像一个容量固定的货架,管理员(GC)会定期清理过期商品。
  • 图片仓库(Bitmap Memory) :专门存放图片数据,在 Android 3.0 前放在 Native 仓库,之后并入 Java 仓库,方便统一管理。
  • Native 仓库(Native Heap) :存放 C/C++ 代码分配的内存,无大小限制,但滥用会导致系统内存紧张,就像无节制扩张的仓库会挤爆工厂。

3.2 仓库清理工的进化:垃圾收集的升级

在 Android 2.3(GingerBread)之前,垃圾收集像 "全厂停工大扫除":所有生产线(线程)都要停止,清理时间可能超过 100ms,导致应用卡顿。

plaintext

c 复制代码
// 旧版GC日志(Stop-the-world)
D/dalvikvm: GC_CONCURRENT freed 2049K, 65% free 3571K/9991K

之后的 GC 像 "分区轮流清理":大部分时间生产线继续运转,只暂停部分区域,清理时间缩短到 5ms 以内。

plaintext

arduino 复制代码
// 新版GC日志(并发GC)
D/dalvikvm: GC_CONCURRENT paused 2ms+2ms  // 仅暂停几毫秒

四、工厂的效率优化:JIT 即时编译的秘密

从 Android 2.2 开始,Dalvik 引入了 "熟练工人"JIT(即时编译):当某个生产流程(代码片段)频繁使用时,JIT 会把它编译成更高效的机器码,就像熟练工人记住了最优操作步骤。

c++

ini 复制代码
// JIT编译核心逻辑(简化版)
void dvmInterpret(Thread* self, const Method* method, JValue* pResult) {
    if (gDvm.executionMode == kExecutionModeJit) {
        // 使用JIT编译后的代码执行
        stdInterp = dvmMterpStd;
    } else {
        // 解释执行
        stdInterp = dvmInterpretStd;
    }
    (*stdInterp)(self, &interpState);
}

与传统 Java 虚拟机(以方法为单位优化)不同,Dalvik 的 JIT 基于 "执行路径" 优化,就像工人不仅记住单个步骤,还记住了整个流程的最优顺序。

五、工厂的跨部门协作:JNI 注册的订单系统

当 Java 车间需要调用 C/C++ 车间的功能(如硬件加速)时,需要通过 JNI(Java Native Interface)建立 "订单系统"。

5.1 订单注册流程

  1. Java 层下订单 :通过System.loadLibrary("nanosleep")加载 C 库。

  2. C 层接单 :在JNI_OnLoad函数中注册订单处理函数。

  3. 订单匹配:将 Java 方法名和签名与 C 函数绑定。

java

java 复制代码
// Java层代码示例
public class ClassWithJni {
    static {
        System.loadLibrary("nanosleep"); // 加载C库
    }
    private native int nanosleep(long seconds, long nanoseconds); // 声明Native方法
}

c

运行

arduino 复制代码
// C层代码示例
static jint shy_luo_jni_ClassWithJni_nanosleep(JNIEnv* env, jobject clazz, jlong seconds, jlong nanoseconds) {
    struct timespec req;
    req.tv_sec = seconds;
    req.tv_nsec = nanoseconds;
    return nanosleep(&req, NULL); // 调用系统函数
}

// 订单表(方法映射)
static const JNINativeMethod method_table[] = {
    {"nanosleep", "(JJ)I", (void*)shy_luo_jni_ClassWithJni_nanosleep}
};

// 注册订单
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) == JNI_OK) {
        jniRegisterNativeMethods(env, "shy/luo/jni/ClassWithJni", method_table, NELEM(method_table));
    }
    return JNI_VERSION_1_4;
}

5.2 订单处理的核心机制

当 Java 方法调用 Native 方法时,虚拟机会通过Method结构体判断方法类型:如果是 Native 方法,直接调用 C 函数;如果是 Java 方法,则由解释器执行。

c++

rust 复制代码
// 方法调用核心逻辑
void dvmCallMethodV(Thread* self, const Method* method, Object* obj, bool fromJni, JValue* pResult, va_list args) {
    if (dvmIsNativeMethod(method)) {
        (*method->nativeFunc)(self->curFrame, pResult, method, self); // 调用Native函数
    } else {
        dvmInterpret(self, method, pResult); // 解释执行Java方法
    }
}

六、工厂的线程管理:工人团队的协作模式

每个 Dalvik 线程对应一个 Linux 线程,就像工厂里的每个工作组对应一个实际的生产线。

6.1 Java 层创建线程

java

typescript 复制代码
// Java层创建线程
new Thread(new Runnable() {
    @Override
    public void run() {
        // 线程任务
    }
}).start();

c++

scss 复制代码
// 底层实现(简化版)
static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult) {
    Object* threadObj = (Object*)args[0];
    dvmCreateInterpThread(threadObj, (int)stackSize); // 创建Dalvik线程
}

6.2 Native 层创建线程

c++

arduino 复制代码
// Native层创建线程
status_t Thread::run(const char* name, int32_t priority, size_t stack) {
    if (mCanCallJava) {
        return createThreadEtc(_threadLoop, this, name, priority, stack, &mThread);
    }
}

七、ART 虚拟机:工厂的智能化升级

虽然文档主要介绍 Dalvik,但 ART(Android Runtime)是 Android 4.4 引入的下一代虚拟机,就像工厂升级了智能生产线:

  • 提前编译(AOT) :不再边运行边编译,而是安装时就把代码编译成机器码,像提前把所有生产流程写成详细手册。
  • 更高效的 GC:引入分代垃圾收集,针对不同生命周期的对象采用不同策略,像把仓库分为 "高频区" 和 "低频区" 分别管理。
  • 优化的内存管理:减少内存碎片,提高分配效率,像重新规划了仓库的货架布局。

总结:从 Dalvik 到 ART 的技术进化

Dalvik 虚拟机就像一个不断进化的智能工厂,通过寄存器指令集、Dex 文件优化、JIT 编译等技术提升效率;Zygote 复制机制和 COW 技术减少了资源消耗;JNI 机制实现了跨语言协作。而 ART 则是这个工厂的智能化升级版,通过提前编译和更高效的内存管理,让 Android 应用运行得更快、更流畅。理解这些原理,就能像熟练的工程师一样优化应用性能,解决内存问题,成为 Android 开发的高手。

相关推荐
雨白4 分钟前
SQLite 数据库的事务与无损升级
android
_一条咸鱼_17 分钟前
Android Runtime即时编译触发条件与阈值深度解析(38)
android·面试·android jetpack
Aridvian18 分钟前
如何使用EventBus?
android
用户20187928316720 分钟前
SystemUI 开发实战故事:手机 "公共设施总管" 的修炼手册
android
火柴就是我25 分钟前
每日见闻之Rust中的引用
android
ajassi200028 分钟前
开源 java android app 开发(十一)调试、发布
android·java·linux·开源
敲代码的剑缘一心2 小时前
手把手教你学会写 Gradle 插件
android·gradle
青蛙娃娃2 小时前
漫画Android:动画是如何实现的?
android·android studio
aningxiaoxixi3 小时前
android 之 CALL
android
用户2018792831674 小时前
Android 核心大管家 ActivityManagerService (AMS)
android