一、概述
ART是Android平台上的新一代运行时,用来代替dalvik。它主要采用了AOT的方法,在apk安装的时候将dalvikbytecode一次性编译成arm本地指令(但是这种AOT与c语言等还是有本质不同的,还是需要虚拟机的环境支持),这样在运行的时候就无需进行任何解释或编译便可直接执行,节省了运行时间,提高了效率,但是在一定程度上使得安装的时间变长,空间占用变大。(本文所有时序图均基于Android M)
二、OAT文件
简单的说,oat文件是嵌套在一个elf文件的格式中的。在elf文件的动态符号表中有三个重要的符号:oatdata、oatexec、oatlastword,分别表示oat的数据区,oat文件中的native code和结束位置。
guoqifa@guoqifa:~$ readelf -s system@priv-app@Mms@Mms.apk@classes.dex
Symbol table '.dynsym' contains 4 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001000 0x95b000 OBJECT GLOBAL DEFAULT 4 oatdata
2: 000000000095c000 0xb3a4c0 OBJECT GLOBAL DEFAULT 5 oatexec
3: 00000000014964bc 4 OBJECT GLOBAL DEFAULT 5 oatlastword
这些关系结构在图中说明的很清楚,简单理解就是在oatdata中,保存有原来的dex文件内容,在头部还保留了寻址到dex文件内容的偏移地址和指向对应的oat class偏移,oat class中还保存了对应的native code的偏移地址,这样也就间接的完成了dexbytecode和native code的对应关系。具体请参考:Android ART Oat文件格式简析(上),Android ART Oat文件格式简析(下),Android运行时ART加载OAT文件的过程分析. 也可自行分析 art\runtime\Oat_file.cc文件中OatFile::Setup()函数来进一步学习OAT文件。(下图来源老罗博客)
oat文件格式三、编译
apk在安装时使用dex2oat来编译。大概流程如下图所示,有两种后端编译模式,可选的是所谓快模式(Quick)或优化模式(Optimizing)。如果没有特别指定的话,编译镜像image使用快模式,而编译一般的应用程序则使用优化模式。
dex2oat compile什么是编译器的后端呢?其实这是LLVM引入的概念。所有的程序先用前端翻译成中间表示层,然后进行优化,最后用后端将优化过的中间表示层代码编译成平台相关的代码。Android虽然没有直接用LLVM编译器(以前也确实用过,但从6.0开始就废弃掉了),但是借鉴了这种编译器的设计结构。值得一提的是,如果使用优化模式,则一定是用PIC模式进行编译。
更具体的compile时序图如下(该时序图只绘出quick编译模式),可自行分析该流程。
Compile Image
四、类加载
ART类的加载通过ClassLinker::DefineClass()函数来完成。该函数会分别调用InsertClass()、LoadClass()、LinkClass()来执行类的加载。
InsertClass()主要是把该类插入class_table_中,方便下次FindClass时直接从LookupClass()中返回结果。LoadClass()用来从指定的DEX文件中加载指定的类,并初始化一个Class对象,Class对象包含了一系列的ArtField对象和ArtMethod对象。LinkClass()用来动态绑定虚函数和接口函数。
类加载完成后,得到的是一个Class对象。这个Class对象关联有一系列的ArtField对象和ArtMethod对象。其中,ArtField对象描述的是成员变量,而ArtMethod对象描述的是成员函数。对于每一个ArtMethod对象,它都有一个解释器入口点和一个本地机器指令入口点。这样,无论一个类方法是通过解释器执行,还是直接以本地机器指令执行,我们都可以以统一的方式来进行调用。
关于类的加载可参考Android运行时ART加载类和方法的过程分析。
Load Class五、方法调用
Zygote孵化应用进程后便会从入口AndroidRuntime.start()进入运行时,进入Java世界。
CallStaticVoidMethodART方法调用如上图所示,最终调用到ArtMethod::Invoke()。ArtMethod正是前面类加载章节中提到的ArtMethod对象,“它都有一个解释器入口点和一个本地机器指令入口点”,Invoke()函数正是通过art_quick_invoke_stub()或art_quick_invoke_static_stub()来调用oat文件中的native code的。art_quick_invoke_stub()是平台相关的汇编函数,比如我的机器该函数定义在art/runtime/arch/arm64/quick_entrypoints_arm64.S文件中。更多方法调用的分析请参考Android运行时ART执行类方法的过程分析.
总结:通过查找相关的oat文件,得到所需要的类和方法,并将其对应的native code的位置放入ArtMethod结构,最后通过Invoke成员完成调用。
六、JNI调用
1. Java调用native方法
ArtMethod对象与真实执行的代码链接的过程主要是通过LinkCode()函数执行的。
void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,
uint32_t class_def_method_index) {
......
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative();
......
}
void ArtMethod::UnregisterNative() {
CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this);
// restore stub to lookup native pointer via dlsym
RegisterNative(GetJniDlsymLookupStub(), false);
}
GetJniDlsymLookupStub()函数返回平台相关的art_jni_dlsym_lookup_stub()汇编函数指针,RegisterNative()将该汇编函数指针注册到ArtMethod中,以上是链接过程。
待JNI调用时便会执行到汇编art_jni_dlsym_lookup_stub()函数,该函数会继续调用artFindNativeMethod()函数。
art_jni_dlsym_lookup_stubextern "C" void* artFindNativeMethod(Thread* self) {
DCHECK_EQ(self, Thread::Current());
#endif
Locks::mutator_lock_->AssertNotHeld(self); // We come here as Native.
ScopedObjectAccess soa(self);
ArtMethod* method = self->GetCurrentMethod(nullptr);
DCHECK(method != nullptr);
// Lookup symbol address for method, on failure we'll return null with an exception set,
// otherwise we return the address of the method we found.
void* native_code = soa.Vm()->FindCodeForNativeMethod(method);
if (native_code == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
} else {
// Register so that future calls don't come here
method->RegisterNative(native_code, false);
return native_code;
}
}
artFindNativeMethod()函数就是查找到相应方法的native code,然后再次注册到ArtMethod中,这样以后再执行的时候就直接跳到了native code执行了。
2. native调用Java
native调用Java是通过JNIEnv->FindClass()、JNIEnv->GetStaticMethodID()、JNIEnv->CallVoidMethod()来查找类,得到相应的方法的ID,然后通过此ID去调用。最终如上面第五章方法调用时序图所示,调用的是ArtMethod::Invoke()。即JNI的这些API其实还是做了一遍ART的类加载和初始化及调用的过程。
七、参考文章
1.也来看看Android的ART运行时
2.Android运行时ART简要介绍和学习计划
3.Android ART Oat文件格式简析
网友评论