一、 说明
据说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(),它来判断什么条件时认为需要编译