ART的函数运行机制
ART的函数运行机制
类的加载
dalvik
的类加载的主要过程如下:
app_process
作为zygote server
通过local socket
处理进程创建请求,zygote server
是在ZygoteInit.main
函数里调用ZygoteInit.runSelectLoop
监听。接收到
zygote client
的fork
请求之后,调用ZygoteConnection.runOnce
,调用Zygote.forkAndSpecialize
创建新进程进程创建之后,由
ZygoteConnection.handleParentProc
来初始化进程,最终会调用ActivityThread.main
函数ActivityThread.main -> ActivityThread.attach -> ActivityThread.bindApplication -> Activity.handleBindApplication,handleBindApplication
会初始化BaseDexClassLoader
。类的加载经过了
ClassLoader.loadClass->BaseDexClassLoader.findClass->DexPathList.findClass->DexFile.loadClassBinaryName->DexFile.defineClassNative->DexFile_defineClassNative
(runtime/native/dalvik_system_DexFile.cc
) 。
这个初始化过程, art
和 dalvik
都是一样的。 art
的 DexFile_defineClassNative
由 ClassLinker
的 DefineClass
来加载类。
1 | static jclass DexFile_defineClassNative(JNIEnv* env, |
类的加载除了创建 Class
只外,还有加载类的字段和方法,这个由 ClassLinker::LoadClass
来完成。
1 | void ClassLinker::LoadClass(Thread* self, |
函数的执行
一旦类的加载完成,那么就可以调用类的成员函数了,之前的解释器运行机制那篇文章介绍过, Java
的执行是以 Method
为执行单元的,所以我们分析 art
的运行机制,其实就是分析 Method
的运行机制。
ActivityThread
是进程在启动的时候传类名,在进程启动之后,由 handleParentProc
执行 main
函数,因此第一个被执行的 java
函数是 ActivityThread.main
。
1 | Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", |
ActivityThread.main
是最终由 AndroidRuntime::callMain
执行。
1 | status_t AndroidRuntime::callMain(const String8& className, jclass clazz, |
实际会调用 JNINativeInterface
的 CallStaticVoidMethod
,上面已经介绍过,该函数的定义在 runtime/jni_internal.cc
里。
1 | static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) { |
InvokeWithVarArgs
是执行函数的入口,定义在 runtime/reflection.cc
,最终是调用了 ArtMethod::Invoke
。
1 | JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, |
我们知道 ART
的运行模式是 AOT
的,在 apk
安装的时候,每个 DexMethod
都会由 dex2oat
编译成目标代码,而不再是虚拟机执行的字节码,但同时 Dex
字节码仍然还在 OAT
里存在,所以 ART
的代码执行既支持 QuickCompiledCode
模式,也同时支持解释器模式以及 JIT
执行模式。看 ArtMethod::Invoke
。
1 | void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, |
Invoke
可以进入 OAT
,Interpreter
模式执行 Method
。如果当前是 Interpreter
模式,就调用 art::interpreter::EnterInterpreterFromInvoke
,如果是 OAT
模式,就调用 art_quick_invoke_stub/art_quick_invoke_static_stub
。EnterInterpreterFromInvoke
函数里会判断是 native
还是解释器执行。
1 | void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receiver, |
这个函数前面部分都在做参数压栈操作,最后几行进入主题,如果不是 Native
,那么调用 Execute
执行; Native
函数则调用 InterpreterJni
。 Execute
就是 art
的解释器代码, Dex
的字节码是通过 ArtMethod::GetCodeItem
函数获得,由 Execute
逐条执行。 InterpreterJni
通过 GetEntryPointFromJni
来获得 native
的函数,并执行。
1 | if (LIKELY(!method->IsNative())) { |
再回调 OAT
的模式, art_quick_invoke_stub/art_quick_invoke_static_stub
最终会调用到 art_quick_invoke_stub_internal
(arch/arm/quick_entrypoints_arm.S
)。
1 | ENTRY art_quick_invoke_stub_internal |
找到 ArtMethod
的 entry_point_from_quick_compiled_code_
字段,这个就是 EntryPointFromQuickCompiledCode
,从而进入 OAT 函数执行。
1 |
|
EntryPointFromQuickCompiledCode
的初始化在 class_linker
的 LoadClassMembers
时调用的 LinkCode
,有下面几种类型
SetEntryPointFromQuickCompiledCode(GetQuickCode()); // 这个是执行 OatMethod
SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); // Dex Method
SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); // Native Method
SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); // method->IsStatic() && !method->IsConstructor()
如果是强制使用了解释器模式,那么执行的是代码 GetQuickToInterpreterBridge(non-static, non-native)
或 GetQuickGenericJniStub(non-static, native)
或 GetQuickResolutionStub(static)
,这几个 EntryPoint
对应的实际执行函数如下。
1 | GetQuickGenericJniStub — artQuickGenericJniTrampoline |
ArtMthod
被 Resolve
之后,如果是走 Oat
模式就会执行 GetQuickCode
。
以上是 EntryPointFromQuickCompiledCode
的情况。
不同的执行模式有不同的 EntryPoint
。
解释器 -
EntryPointFromInterpreter
在
interpreter/interpreter_common.cc
里会在执行解释器函数时,会获得ArtMethod
的Interpret EntryPoint
执行Jni -
EntryPointFromJni
interpreter/interpreter.cc
,InterpreterJni
函数会获得ArtMethod的Jni EntryPoint
执行Oat -
EntryPointFromQuickCompiledCode
DexCache
在Init
的时候会将Method
都初始化为ResolutionMethod
,这个Resolution Method
是没有dex method id
的,是个RuntimeMethod
,这是lazy load method
,运行时resolve
之后才会替换成实际的ArtMethod
。
1 | void DexCache::Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings, |
resolution method
的 EntryPointFromQuickCompiledCode
指向 GetQuickResolutionStub
,意思就是一开始,这些函数的执行点都是从 artQuickResolutionTrampoline
开始。
1 | // Lazily resolve a method for quick. Called by stub code. |
上面代码可知,找到当前 ArtMethod
的流程大致的逻辑就是,根据 caller
函数 ArtMethod
的 dex
代码,可以找到这个 ArtMethod
的函数调用类型(INVOKE_DIRECT, INVOKE_STATIC, INVOKE_SUPER, INVOKE_VIRTUAL etc
.),不同的类型查找的方式不一样,比如 Virtual Method
要从虚表里找,Super Method
要从父类的 Method
里去找,找到之后调用 ClassLinker
的 ResolveMethod
来解析,解析出来的 ArtMethod
的就是上面 LinkCode
过的 ArtMethod
。
下面就是 ResolveMethod
函数的实现, Calss
查找到 Method
,之后在赋值到 DexCache
里,这样下次再执行就能直接找到 Resolved Method
。
1 | ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, |
至此,Art Method 的执行机制就算介绍完了,我们对整个函数执行机制都有个全局的概念了。