Android运行时ART加载OAT文件的过程分析资料Word文档格式.docx
《Android运行时ART加载OAT文件的过程分析资料Word文档格式.docx》由会员分享,可在线阅读,更多相关《Android运行时ART加载OAT文件的过程分析资料Word文档格式.docx(31页珍藏版)》请在冰豆网上搜索。
--zip-fd="
)+MAX_INT_LEN];
charzip_location_arg[strlen("
--zip-location="
)+PKG_PATH_MAX];
charoat_fd_arg[strlen("
--oat-fd="
charoat_location_arg[strlen("
--oat-name="
sprintf(zip_fd_arg,"
--zip-fd=%d"
zip_fd);
sprintf(zip_location_arg,"
--zip-location=%s"
input_file_name);
sprintf(oat_fd_arg,"
--oat-fd=%d"
oat_fd);
sprintf(oat_location_arg,"
--oat-location=%s"
output_file_name);
ALOGV("
Running%sin=%sout=%s\n"
DEX2OAT_BIN,input_file_name,output_file_name);
execl(DEX2OAT_BIN,DEX2OAT_BIN,
zip_fd_arg,zip_location_arg,
oat_fd_arg,oat_location_arg,
(char*)NULL);
ALOGE("
execl(%s)failed:
%s\n"
DEX2OAT_BIN,strerror(errno));
}
这个函数定义在文件frameworks/native/cmds/installd/commands.c中。
其中,参数zip_fd和oat_fd都是打开文件描述符,指向的分别是正在安装的APK文件和要生成的OAT文件。
OAT文件的生成过程主要就是涉及到将包含在APK里面的classes.dex文件的DEX字节码翻译成本地机器指令。
这相当于是编写一个输入文件为DEX、输出文件为OAT的编译器。
这个编译器是基于LLVM编译框架开发的。
编译器的工作原理比较高大上,所幸的是它不会影响到我们接下来的分析,因此我们就略过DEX字节码翻译成本地机器指令的过程,假设它很愉快地完成了。
APK安装过程中生成的OAT文件的输入只有一个DEX文件,也就是来自于打包在要安装的APK文件里面的classes.dex文件。
实际上,一个OAT文件是可以由若干个DEX生成的。
这意味着在生成的OAT文件的oatdata段中,包含有多个DEX文件。
那么,在什么情况下,会生成包含多个DEX文件的OAT文件呢?
从前面一文可以知道,当我们选择了ART运行时时,Zygote进程在启动的过程中,会调用libart.so里面的函数JNI_CreateJavaVM来创建一个ART虚拟机。
函数JNI_CreateJavaVM的实现如下所示:
extern"
C"
jintJNI_CreateJavaVM(JavaVM**p_vm,JNIEnv**p_env,void*vm_args){
constJavaVMInitArgs*args=static_cast<
JavaVMInitArgs*>
(vm_args);
if(IsBadJniVersion(args->
version)){
LOG(ERROR)<
<
"
BadJNIversionpassedtoCreateJavaVM:
<
args->
version;
returnJNI_EVERSION;
}
Runtime:
:
Optionsoptions;
for(inti=0;
i<
nOptions;
++i){
JavaVMOption*option=&
args->
options[i];
options.push_back(std:
make_pair(std:
string(option->
optionString),option->
extraInfo));
boolignore_unrecognized=args->
ignoreUnrecognized;
if(!
Runtime:
Create(options,ignore_unrecognized)){
returnJNI_ERR;
Runtime*runtime=Runtime:
Current();
boolstarted=runtime->
Start();
started){
deleteThread:
Current()->
GetJniEnv();
deleteruntime->
GetJavaVM();
LOG(WARNING)<
CreateJavaVMfailed"
*p_env=Thread:
*p_vm=runtime->
returnJNI_OK;
这个函数定义在文件art/runtime/jni_internal.cc中。
参数vm_args用作ART虚拟机的启动参数,它被转换为一个JavaVMInitArgs对象后,再按照Key-Value的组织形式保存一个Options向量中,并且以该向量作为参数传递给Runtime类的静态成员函数Create。
Runtime类的静态成员函数Create负责在进程中创建一个ART虚拟机。
创建成功后,就调用Runtime类的另外一个静态成员函数Start启动该ART虚拟机。
注意,这个创建ART虚拟的动作只会在Zygote进程中执行,SystemServer系统进程以及Android应用程序进程的ART虚拟机都是直接从Zygote进程fork出来共享的。
这与Dalvik虚拟机的创建方式是完全一样的。
接下来我们就重点分析Runtime类的静态成员函数Create,它的实现如下所示:
boolRuntime:
Create(constOptions&
options,boolignore_unrecognized){
//TODO:
acquireastaticmutexonRuntimetoavoidracing.
if(Runtime:
instance_!
=NULL){
returnfalse;
InitLogging(NULL);
//CallsLocks:
Init()asasideeffect.
instance_=newRuntime;
instance_->
Init(options,ignore_unrecognized)){
deleteinstance_;
instance_=NULL;
returntrue;
这个函数定义在文件art/runtime/runtime.cc中。
instance_是Runtime类的静态成员变量,它指向进程中的一个Runtime单例。
这个Runtime单例描述的就是当前进程的ART虚拟机实例。
函数首先判断当前进程是否已经创建有一个ART虚拟机实例了。
如果有的话,函数就立即返回。
否则的话,就创建一个ART虚拟机实例,并且保存在Runtime类的静态成员变量instance_中,最后调用Runtime类的成员函数Init对该新创建的ART虚拟机进行初始化。
Runtime类的成员函数Init的实现如下所示:
Init(constOptions&
raw_options,boolignore_unrecognized){
......
UniquePtr<
ParsedOptions>
options(ParsedOptions:
Create(raw_options,ignore_unrecognized));
heap_=newgc:
Heap(options->
heap_initial_size_,
options->
heap_growth_limit_,
heap_min_free_,
heap_max_free_,
heap_target_utilization_,
heap_maximum_size_,
image_,
is_concurrent_gc_enabled_,
parallel_gc_threads_,
conc_gc_threads_,
low_memory_mode_,
long_pause_log_threshold_,
long_gc_log_threshold_,
ignore_max_footprint_);
java_vm_=newJavaVMExt(this,options.get());
Thread*self=Thread:
Attach("
main"
false,NULL,false);
if(GetHeap()->
GetContinuousSpaces()[0]->
IsImageSpace()){
class_linker_=ClassLinker:
CreateFromImage(intern_table_);
}else{
CreateFromCompiler(*options->
boot_class_path_,intern_table_);
Runtime类的成员函数Init首先调用ParsedOptions类的静态成员函数Create对ART虚拟机的启动参数raw_options进行解析。
解析后得到的参数保存在一个ParsedOptions对象中,接下来就根据这些参数一个ART虚拟机堆。
ART虚拟机堆使用一个Heap对象来描述。
创建好ART虚拟机堆后,Runtime类的成员函数Init接着又创建了一个JavaVMExt实例。
这个JavaVMExt实例最终是要返回给调用者的,使得调用者可以通过该JavaVMExt实例来和ART虚拟机交互。
再接下来,Runtime类的成员函数Init通过Thread类的成员函数Attach将当前线程作为ART虚拟机的主线程,使得当前线程可以调用ART虚拟机提供的JNI接口。
Runtime类的成员函数GetHeap返回的便是当前ART虚拟机的堆,也就是前面创建的ART虚拟机堆。
通过调用Heap类的成员函数GetContinuousSpaces可以获得堆里面的连续空间列表。
如果这个列表的第一个连续空间是一个Image空间,那么就调用ClassLinker类的静态成员函数CreateFromImage来创建一个ClassLinker对象。
否则的话,上述ClassLinker对象就要通过ClassLinker类的另外一个静态成员函数CreateFromCompiler来创建。
创建出来的ClassLinker对象是后面ART虚拟机加载加载Java类时要用到的。
后面我们分析ART虚拟机的垃圾收集机制时会看到,ART虚拟机的堆包含有三个连续空间和一个不连续空间。
三个连续空间分别用来分配不同的对象。
当第一个连续空间不是Image空间时,就表明当前进程不是Zygote进程,而是安装应用程序时启动的一个dex2oat进程。
安装应用程序时启动的dex2oat进程也会在内部创建一个ART虚拟机,不过这个ART虚拟机是用来将DEX字节码编译成本地机器指令的,而Zygote进程创建的ART虚拟机是用来运行应用程序的。
接下来我们主要分析ParsedOptions类的静态成员函数Create和ART虚拟机堆Heap的构造函数,以便可以了解ART虚拟机的启动参数解析过程和ART虚拟机的堆创建过程。
ParsedOptions类的静态成员函数Create的实现如下所示:
ParsedOptions*Runtime:
ParsedOptions:
parsed(newParsedOptions());
constchar*boot_class_path_string=getenv("
BOOTCLASSPATH"
);
if(boot_class_path_string!
parsed->
boot_class_path_string_=boot_class_path_string;
is_compiler_=false;
for(size_ti=0;
options.size();
conststd:
stringoption(options[i].first);
if(StartsWith(option,"
-Xbootclasspath:
"
)){
boot_class_path_string_=option.substr(strlen("
)).data();
}elseif(option=="
bootclasspath"
){
boot_class_path_
=reinterpret_cast<
conststd:
vector<
constDexFile*>
*>
(options[i].second);
}elseif(StartsWith(option,"
-Ximage:
image_=option.substr(strlen("
}elseif(......){
compiler"
is_compiler_=true;
parsed->
is_compiler_&
&
image_.empty()){
image_+=GetAndroidRoot();
image_+="
/framework/boot.art"
returnparsed.release();
ART虚拟机的启动参数比较多,这里我们只关注两个:
-Xbootclasspath、-Ximage和compiler。
参数-Xbootclasspath用来指定启动类路径。
如果没有指定启动类路径,那么默认的启动类路径就通过环境变量BOOTCLASSPATH来获得。
参数-Ximage用来指定ART虚拟机所使用的Image文件。
这个Image是用来启动ART虚拟机的。
参数compiler用来指定当前要创建的ART虚拟机是用来将DEX字节码编译成本地机器指令的。
如果没有指定Image文件,并且当前创建的ART虚拟机又不是用来编译DEX字节码的,那么就将该Image文件指定为设备上的/system/framework/boot.art文件。
我们知道,system分区的文件都是在制作ROM时打包进去的。
这样上述代码的逻辑就是说,如果没有指定Image文件,那么将system分区预先准备好的framework/boot.art文件作为Image文件来启动ART虚拟机。
不过,/system/framework/boot.art文件可能是不存在的。
在这种情况下,就需要生成一个新的Image文件。
这个Image文件就是一个包含了多个DEX文件的OAT文件。
接下来通过分析ART虚拟机堆的创建过程就会清楚地看到这一点。
Heap类的构造函数的实现如下所示:
Heap:
Heap(size_tinitial_size,size_tgrowth_limit,size_tmin_free,size_tmax_free,
doubletarget_utilization,size_tcapacity,conststd:
string&
original_image_file_name,
boolconcurrent_gc,size_tparallel_gc_threads,size_tconc_gc_threads,
boollow_memory_mode,size_tlong_pause_log_threshold,size_tlong_gc_log_threshold,
boolignore_max_footprint)
:
......{
std:
stringimage_file_name(original_image_file_name);
image_file_name.empty()){
space:
ImageSpace*image_space=space:
ImageSpace:
Create(image_file_name);
AddContinuousSpace(image_space);
这个函数定义在文件art/runtime/gc/heap.cc中。
ART虚拟机堆的详细创建过程我们在后面分析ART虚拟机的垃圾收集机制时再分析,这里只关注与Image文件相关的逻辑。
参数original_image_file_name描述的就是前面提到的Image文件的路径。
如果它的值不等于空的话,那么就以它为参数,调用ImageSpace类的静态成员函数Create创建一个Image空间,并且调用Heap类的成员函数AddContinuousSpace将该Image空间作为本进程的ART虚拟机堆的第一个连续空间。
接下来我们继续分析ImageSpace类的静态成员函数Create,它的实现如下所示:
ImageSpace*ImageSpace:
Create(conststd:
original_image_file_name){
if(OS:
FileExists(original_image_file_name.c_str())){
//Ifthe/systemfileexists,itshouldbeup-to-date,don'
ttrytogenerate
returnspace: