Dalvik的JIT编译流程 & ART的dex2oat流程

一.Dalvik的JIT编译流程
Dalvik JIT是一个tracing JIT(也叫trace-based JIT),其中间表示分为两种,MIR与LIR。MIR与LIR节点各自形成链表,分别被组织在BasicBlock与CompilationUnit中。
编译流程是:
0、创建CompilationUnit对象(一个trace对应一个CompilationUnit)来存放一次编译中需要的信息:CompilationUnit cUnit; ;
1、将dex文件中的Dalvik字节码解码为DecodedInstruction,并创建对应的MIR节点(一条dalvik字节码对应若干个MIR):parseInsn(...);
2、并创建相应的BasicBlock对象,将MIR塞进去(一个trace被划分为多个BasicBlock,每个BasicBlock包含多个MIR):dvmCompilerAppendMIR(...);
3、定位基本块的边界: findBlockBoundary(...);
4、确定控制流关系,将基本块连接起来构成控制流图(CFG),并添加恢复解释器状态和异常处理用的基本块;
5、将基本块都加到CompilationUnit里去,如入口块,出口块,代码块,Chaining Cell;
6、SSA形式转换:dvmCompilerNonLoopAnalysis(...);
7、将MIR转换为LIR(带有局部优化和全局优化):dvmCompilerMIR2LIR(...);
8、从LIR生成机器码:dvmCompilerAssembleLIR(...)。

二.ART的dex2oat流程

Kitkat上用的ART都不是用Portable后端(这个后端根本还是半成品),而是用Quick后端。这个编译器源自Dalvik VM的JIT编译器,被大幅度改成了method-based AOT编译器。而AOSP里最新的ART源码里已经彻底删除了整个Portable后端(以及另一个使用LLVM的后端“Sea IR”),包括里面的LLVM源码。到此为止ART已经跟LLVM彻底没关系。
ART只有一个用C++写的可移植版解释器。ART的AOT编译器(dex2oat)并不会一开始就把所以方法都编译掉,而是选择一部分方法编译为native code,另一部分做dex-to-dex的转换然后就把dex存到OAT文件里,选择不做AOT的method的原则是init method或者太大的method,大部分Java方法都会用Quick编译器编译为native代码,但是有些特定的方法(例如类初始化特殊方法()V)会不编译而总是让解释器执行。不过随着新的Optimizing编译器成熟起来,不知道编译策略上是不是也有更新。
ART的运行时含有sampling profiler(还在开发中。架构先放在那里了),会统计方法的热度。如果有一个程序的某个方法比较热但尚未被ART的AOT编译器编译,则下次dex2oat有机会运行的时候会把这个方法也编译为native。
art/dex2oat/dex2oat.cc中:
int main(...) --> art::dex2oat(...) --> CompileApp(dex2oat) --> dex2oat.Compile() (创建幷调用compiler driver,编译所有dex文件) --> driver_->CompileAll(...) -->
art/compiler/driver/compiler_driver.cc中:
Compile(...) --> CompileDexFile(...) --> context.ForAll(...) --> CompilerDriver::CompileClass(...) --> driver->CompileMethod(...) --> compiler_->Compile(...) -->
函数CompileClass()是一个回调函数,启动了多线程,执行真正的编译过程, 函数的主要过程就是通过读取class中的数据,利用迭代器遍历每个DirectMethod和VirtualMethod,然后分别对每个Method作为单元利用CompilerDriver::CompileMethod进行编译。
art/compiler/dex/quick/quick_compiler.cc中:
QuickCompiler::Compile(...)
ART中与MIPS相关的目录:
art/runtime/arch/mips/
art/compiler/dex/quick/mips/
art/compiler/utils/mips/
art/compiler/jni/quick/mips/
runtime/arch/mips/registers_mips.h中定义了mips的32个通用寄存器、32个单精度浮点寄存器。
compiler/utils/mips/assembler_mips.cc中定义了所有MIPS指令及其发射到AssembleBuffer。
三.Arena Allocation 一种非传统的内存管理方法。它通过顺序化分配内存,内存数据分块等特性使内存碎片粗化,有效改善了内存碎片导致的Full GC问题。
原理:
创建一个大小固定的bytes数组和一个偏移量,默认值为0。
分配对象时,将新对象的data bytes复制到数组中,数组的起始位置是偏移量,复制完成后为偏移量自增data.length的长度,这样做是防止下次复制数据时不会覆盖掉老数据(append)。
当一个数组被充满时,创建一个新的数组。清理时,只需要释放掉这些数组,即可得到固定的大块连续内存。
在Arena Allocation方案中,数组的大小影响空间连续性,越大内存连续性越好,但内存平均利用率会降低。
【Dalvik的JIT编译流程 & ART的dex2oat流程】 进入Android Dalvik虚拟机之Dalvik汇编语言基础: http://my.oschina.net/fhd/blog/365337
[Dalvik VM] Dalvik VM的JIT编译器的资料堆积: http://hllvm.group.iteye.com/group/topic/17798
Android 中的 LLVM 主要做什么: https://www.zhihu.com/question/26646049/answer/36455706
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器: http://rednaxelafx.iteye.com/blog/492667
extern "c"用法解析: http://www.jianshu.com/p/5d2eeeb93590

    推荐阅读