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 -
EntryPointFromJniinterpreter/interpreter.cc,InterpreterJni函数会获得ArtMethod的Jni EntryPoint执行Oat -
EntryPointFromQuickCompiledCodeDexCache在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 的执行机制就算介绍完了,我们对整个函数执行机制都有个全局的概念了。