浅析 dalvik 虚拟机 JIT 技术的实现

#移动开发 #Android

一、说明
据说 Android 2.2 的虚拟机 dalvik 使用了 JIT 技术,使其运行 速度快了 5 倍。dalvik

解释并执行程序,JIT 技术主要是对多次运行的代码进行编译,当再次调用时使用编译之后的机器码,而不是每次都解释,以节约时间。5

倍是测试程序测出的值,并不是说程序运行速度也能达到 5

倍,这是因为测试程序有很多的重复调用和循环,而一般程序主要是顺序执行的,而且它是一边运行,一边编译,一开始的时候提速不多,所以真正运行程序速度提高不是特别明显。

二、JAVA 虚拟机主要的优化技术

1. JIT(Just In Time) 最开始指在执行前编译,但是到现在已经发展成为,一开始解释执行,只有被多次调用的程序段才被编译,编译后存放在内存中,下次直接执行编译后的机器码

  1. method 方式:以函数或方法为单位进行编译

  2. trace 方式:以 trace 为单位进行编译(可以把循环中的内容作为单位编译),此方法也包含 method

2. AOT(Ahead Of Time) 在程序下载到本地时就编译成机器码,并存储在本地存在硬盘上,以加快运行程度,用此种方式,可执行的程序会变大四五倍

三、dalvik 中 JIT 的实现
每启动一个应用程序,都会相应地启动一个 dalvik 虚拟机,启动时会建立 JIT

线程,一直在后台运行。当某段代码被调用时,虚拟机会判断它是否需要编译成机器码,如果需要,就做一个标记,JIT

线程不断判断此标记,如果发现被设定就把它编译成机器码,并将其机器码地址及相关信息放入 entry table

中,下次执行到此就跳到机器码段执行,而不再解释执行,从而提高速度。

四、dalvik JIT 部分代码分析
android 2.2 代码还未发布,而 2.1 代码中已经加入 JIT 部分,只是默认未打开,以 2.1

代码为例对其原理进行分析,源码位置:$ANDROID_SRC/dalvik/vm/

1. 核心文件

  1. vm/interp/Jit.c
    供外界调用的入口

  2. vm/compiler/compiler.c
    核心函数的实现

  3. vm/compiler/Frontend.c
    编译 method 和 trace 的实现

2. 全局变量

  1. gDvmJit
    所有与 JIT 相关的数据及结构都存储于此,各个程序通过它访问 JIT 资源

3. 开关

  1. dvmCompilerStartup() (vm/compiler/Compiler.c) 在虚拟机启动时调用

  2. dvmCompilerShutdown() (vm/compiler/Compiler.c) 在虚拟机关闭时调用

4. 线程

  1. compilerThreadStart() (vm/compiler/Compiler.c)
    被 dvmCompilerStartup() 调用,在虚拟机运行过程中一直生存的线程,while

循环判断是否有代码需要编译,如果有,则调用 dvmCompilerDoWork() 对其进行编译

5. 编译成字节码
doCompilerdoWork() 又分 method 和 trace 两种情况进行编译

  1. dvmCompilerMethod() (vm/compiler/Frongend.c)

  2. dvmCompilerTrace() (vm/compiler/Frongend.c)
    其中的:dvmCompilerMIR2LIR(),dvmCompilerAssembleLIR()(MIR 和 LIR

都是解释层的中间表示)逐层翻译成机器码

6. 编译后字节码的存储

  1. dvmcompilerSetupCodeCache() (vm/compiler/Compiler.c)
    gDvmJit.codeCache:使用 mmap 分配(1024x1024),用于存在编译后的代码
    gDvmJit.codeCacheByteUsed:codeCache 的使用情况
    gDvmJit.codeCacheFull:codeCache 是否已写满
    gDvmJit.pJitEntryTable:entry 表,每个 trace 对应一个 entry

7. 其它线程对 JIT 资源的访问
以下是一个示例,说明从主函数到 JIT 的调用关系如下: AndroidRuntime::Start()->startVm()->_JNIEnv::CallStaticVoidMethod()->Check_CallStaticVoidMethodV()->CallStaticVoidMethodV()->dvmCallMethodV()->dvmInterpret()->dvmMterpStd()->dalvik_mterp()->Dalvik_java_lang_reflect_Method_invokeNative()->dvmInvokeMethod()->dvmInterpret()->dvmJitCheckTraceRequest()->dvmjitLookupAndAdd()
主要函数解释如下:

  1. dvmJitLookupAndAdd()
    判断是否有相对某段程序的字节码可用,如果有则返回其地址,如果没有,则做标记,以通知编译线程对其进行编译

  2. dvmCheckJit()
    此函数被 define 成 CHECK_JIT(),它来判断什么条件时认为需要编译

(转载请注明出处:http://xy0811.spaces.live.com