Android6.0中ART执行类方法的过程分析一

来源:互联网 发布:电子书资源 知乎 编辑:程序博客网 时间:2024/05/29 13:33

参考:http://blog.csdn.net/luoshengyang/article/details/40289405 罗升阳老师的 Android运行时ART执行类方法的过程分析 一文所写,主要将代码实现部分做了改动。

OatFile* OatFile::Open函数用来加载oat文件,原本的后端是portable和quick,而现在的是quick和optimizing,现在的这两个后端在open函数实现上并没有区别,选择的加载器都是安卓自带的android_dlopen_ext动态库加载器。而通过Portable后端和Quick后端生成的OAT文件的本质区别在于,前者使用标准的动态链接器加载,而后者使用自定义的加载器加载。
optimizing和quick的区分是在dex2oat的CompilerDriver中,根据dex2oat的参数不同,选择具体的compiler-kQuick还是kOptimizing。
在前面分析Dalvik虚拟机的文章Dalvik虚拟机进程和线程的创建过程分析中,我们提到每一个Dalvik虚拟机线程在内部都通过一个Thread对象描述。这个Thread对象包含了一些与虚拟机相关的信息。例如,JNI函数调用函数表。在ART运行时中创建的线程,和Davik虚拟机线程一样,在内部也会通过一个Thread对象来描述。这个新的Thread对象内部除了定义JNI调用函数表之外,还定义了我们在上面提到的外部函数调用跳转表。
在前面Android运行时ART加载OAT文件的过程分析一文,我们提到了ART运行时的启动和初始化过程。其中的一个初始化过程便是将主线程关联到ART运行时去,如下所示:

 782 bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) {    ... 967   java_vm_ = new JavaVMExt(this, runtime_options);     ... 971   // ClassLinker needs an attached thread, but we can't fully attach a thread without creating 972   // objects. We can't supply a thread group yet; it will be fixed later. Since we are the main 973   // thread, we do not get a java peer. 974   Thread* self = Thread::Attach("main", false, nullptr, false);     ...1124   return true;1125 }

这个函数定义在文件art/runtime/runtime.cc中。
在Runtime类的成员函数Init中,通过调用Thread类的静态成员函数Attach将当前线程,也就是主线程,关联到ART运行时去。在关联的过程中,就会初始化一个外部库函数调用跳转表。
Thread类的静态成员函数Attach的实现如下所示:

 513 Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group, 514                        bool create_peer) { 515   Runtime* runtime = Runtime::Current();    ... 520   Thread* self;  521   { 522     MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_); 523     if (runtime->IsShuttingDownLocked()) { 524       LOG(ERROR) << "Thread attaching while runtime is shutting down: " << thread_name; 525       return nullptr; 526     } else { 527       Runtime::Current()->StartThreadBirth(); 528       self = new Thread(as_daemon); 529       bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM()); 530       Runtime::Current()->EndThreadBirth(); 531       if (!init_success) { 532         delete self; 533         return nullptr; 534       } 535     } 536   }    ... 576   return self; 577 }

这个函数定义在文件art/runtime/thread.cc中。
在Thread类的静态成员函数Attach中,最重要的就是创建了一个Thread对象来描述当前被关联到ART运行时的线程。创建了这个Thread对象之后,马上就调用它的成员函数Init来对它进行初始化。
Thread类的成员函数Init的实现如下所示:

 471 bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {    ...  488   InitTlsEntryPoints(); 489   RemoveSuspendTrigger(); 490   InitCardTable();//jvm的卡表机制。参见:http://blog.csdn.net/lihuifeng/article/details/51681089 498   if (jni_env_ext != nullptr) { 499     DCHECK_EQ(jni_env_ext->vm, java_vm); 500     DCHECK_EQ(jni_env_ext->self, this); 501     tlsPtr_.jni_env = jni_env_ext; 502   } else { 503     tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm); 504     if (tlsPtr_.jni_env == nullptr) { 505       return false; 506     } 507   } 508  509   thread_list->Register(this); 510   return true; 511 }

这个函数定义在文件art/runtime/thread.cc中。
Thread类的成员函数Init除了给当前的线程创建一个JNIEnvExt对象来描述它的JNI调用接口之外,还通过调用另外一个成员函数InitTlsEntryPoints来初始化一个外部库函数调用跳转表。
Thread类的成员函数InitTlsEntryPoints的实现如下所示:

  97 void Thread::InitTlsEntryPoints() {    ... 105   InitEntryPoints(&tlsPtr_.interpreter_entrypoints, &tlsPtr_.jni_entrypoints, 106                   &tlsPtr_.quick_entrypoints); 107 }

这个函数定义在文件art/runtime/thread.cc中。
调用的InitEntryPoints函数,该函数与架构相关,选择的是在~/android-6.0.1_r62/art/runtime/arch/arm64/entrypoints_init_arm64.cc中实现的:

 40 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, 41                      QuickEntryPoints* qpoints) {...}

Thread类定义了**三个成员变量**interpreter_entrypoints_、jni_entrypoints_和quick_entrypoints_,如下所示:

 139 class Thread {     ...1241     // Entrypoint function pointers.1242     // TODO: move this to more of a global offset table model to avoid per-thread duplication.1243     InterpreterEntryPoints interpreter_entrypoints;1244     JniEntryPoints jni_entrypoints;1245     QuickEntryPoints quick_entrypoints;    ...    };

Thread类的声明定义在文件art/runtime/thread.h中。
Thread类将外部库函数调用跳转表划分为3个,其中,interpreter_entrypoints_描述的是解释器要用到的跳转表,jni_entrypoints_描述的是JNI调用相关的跳转表,而quick_entrypoints_描述的是Quick后端生成的本地机器指令要用到的跳转表。
回到Thread类的成员函数InitTlsEntryPoints中,它通过调用一个全局函数InitEntryPoints来初始化上述的3个跳转表。全局函数InitEntryPoints的实现是和CPU体系结构相关的,因为跳转表里面的函数调用入口是用汇编语言来实现的。
以ARM64架构为例:

 40 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, 41                      QuickEntryPoints* qpoints) { 42   // Interpreter 43   ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge; 44   ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge; 45  46   // JNI 47   jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub; 48  49   // Alloc 50   ResetQuickAllocEntryPoints(qpoints); 51  52   // Cast 53   qpoints->pInstanceofNonTrivial = art_quick_assignable_from_code; 54   qpoints->pCheckCast = art_quick_check_cast; 55  56   // DexCache 57   qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage; 58   qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access; 59   qpoints->pInitializeType = art_quick_initialize_type; 60   qpoints->pResolveString = art_quick_resolve_string; 61  62   // Field 63   qpoints->pSet8Instance = art_quick_set8_instance; 64   qpoints->pSet8Static = art_quick_set8_static; 65   qpoints->pSet16Instance = art_quick_set16_instance; 66   qpoints->pSet16Static = art_quick_set16_static; 67   qpoints->pSet32Instance = art_quick_set32_instance; 68   qpoints->pSet32Static = art_quick_set32_static; 69   qpoints->pSet64Instance = art_quick_set64_instance; 70   qpoints->pSet64Static = art_quick_set64_static; 71   qpoints->pSetObjInstance = art_quick_set_obj_instance; 72   qpoints->pSetObjStatic = art_quick_set_obj_static; 73   qpoints->pGetBooleanInstance = art_quick_get_boolean_instance; 74   qpoints->pGetByteInstance = art_quick_get_byte_instance; 75   qpoints->pGetCharInstance = art_quick_get_char_instance; 76   qpoints->pGetShortInstance = art_quick_get_short_instance; 77   qpoints->pGet32Instance = art_quick_get32_instance; 78   qpoints->pGet64Instance = art_quick_get64_instance; 79   qpoints->pGetObjInstance = art_quick_get_obj_instance; 80   qpoints->pGetBooleanStatic = art_quick_get_boolean_static; 81   qpoints->pGetByteStatic = art_quick_get_byte_static; 82   qpoints->pGetCharStatic = art_quick_get_char_static; 83   qpoints->pGetShortStatic = art_quick_get_short_static; 84   qpoints->pGet32Static = art_quick_get32_static; 85   qpoints->pGet64Static = art_quick_get64_static; 86   qpoints->pGetObjStatic = art_quick_get_obj_static; 87  88   // Array 89   qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; 90   qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; 91   qpoints->pAputObject = art_quick_aput_obj; 92   qpoints->pHandleFillArrayData = art_quick_handle_fill_data; 93  94   // JNI 95   qpoints->pJniMethodStart = JniMethodStart; 96   qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized; 97   qpoints->pJniMethodEnd = JniMethodEnd; 98   qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized; 99   qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;100   qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;101   qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;102 103   // Locks104   qpoints->pLockObject = art_quick_lock_object;105   qpoints->pUnlockObject = art_quick_unlock_object;106 107   // Math108   // TODO null entrypoints not needed for ARM64 - generate inline.109   qpoints->pCmpgDouble = nullptr;110   qpoints->pCmpgFloat = nullptr;111   qpoints->pCmplDouble = nullptr;112   qpoints->pCmplFloat = nullptr;113   qpoints->pFmod = art_quick_fmod;114   qpoints->pL2d = nullptr;115   qpoints->pFmodf = art_quick_fmodf;116   qpoints->pL2f = nullptr;117   qpoints->pD2iz = nullptr;118   qpoints->pF2iz = nullptr;119   qpoints->pIdivmod = nullptr;120   qpoints->pD2l = nullptr;121   qpoints->pF2l = nullptr;122   qpoints->pLdiv = nullptr;123   qpoints->pLmod = nullptr;124   qpoints->pLmul = nullptr;125   qpoints->pShlLong = nullptr;126   qpoints->pShrLong = nullptr;127   qpoints->pUshrLong = nullptr;128 129   // Intrinsics130   qpoints->pIndexOf = art_quick_indexof;131   qpoints->pStringCompareTo = art_quick_string_compareto;132   qpoints->pMemcpy = art_quick_memcpy;133 134   // Invocation135   qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;136   qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;137   qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;138   qpoints->pInvokeDirectTrampolineWithAccessCheck =139       art_quick_invoke_direct_trampoline_with_access_check;140   qpoints->pInvokeInterfaceTrampolineWithAccessCheck =141       art_quick_invoke_interface_trampoline_with_access_check;142   qpoints->pInvokeStaticTrampolineWithAccessCheck =143       art_quick_invoke_static_trampoline_with_access_check;144   qpoints->pInvokeSuperTrampolineWithAccessCheck =145       art_quick_invoke_super_trampoline_with_access_check;146   qpoints->pInvokeVirtualTrampolineWithAccessCheck =147       art_quick_invoke_virtual_trampoline_with_access_check;148 149   // Thread150   qpoints->pTestSuspend = art_quick_test_suspend;151 152   // Throws153   qpoints->pDeliverException = art_quick_deliver_exception;154   qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;155   qpoints->pThrowDivZero = art_quick_throw_div_zero;156   qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;157   qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;158   qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;159 160   // Deoptimize161   qpoints->pDeoptimize = art_quick_deoptimize;162 163   // Read barrier164   qpoints->pReadBarrierJni = ReadBarrierJni;165 };

这个函数定义在文件art/runtime/arch/arm64/entrypoints_init_arm64.cc中。
从函数InitEntryPoints的实现就可以看到Quick后端要使用到的外部库函数调用跳转表的初始化过程了。例如,如果在生成的本地机器指令中,需要调用一个JNI函数,那么就需要通过art_jni_dlsym_lookup_stub函数来间接地调用,以便可以找到正确的JNI函数来调用。
此外,我们还可以看到,解释器要用到的跳转表只包含了两项,分别是artInterpreterToInterpreterBridge和artInterpreterToCompiledCodeBridge。前者用来从一个解释执行的类方法跳到另外一个也是解释执行的类方法去执行,后者用来从一个解释执行的类方法跳到另外一个以本地机器指令执行的类方法去执行。
剩下的其它代码均是用来初始化Quick后端生成的本地机器指令要用到的跳转表,它包含的项非常多,但是可以划分为Alloc(对象分配)、Cast(类型转换)、DexCache(Dex缓访问)、Field(成员变量访问)、FillArray(数组填充)、JNI(JNI函数调用)、Locks(锁)、Math(数学计算)、Intrinsics(内建函数调用)、Invocation(类方法调用)、Thread(线程操作),Throws(异常处理),Deoptimize(反优化)和Read barrier(读屏障)等14类。
有了这些跳转表之后,当我们需要在生成的本地机器指令中调用一个外部库提供的函数时,只要找到用来描述当前线程的Thread对象,然后再根据上述的四个跳转表在该Thread对象内的偏移位置,那么就很容易找到所需要的跳转项了。

在前面Android运行时ART加载类和方法的过程分析这篇文章中,我们提到,在类的加载过程中,需要对类的各个方法进行链接,实际上就是确定它们是通过解释器来执行,还是以本地机器指令来直接执行,如下所示:

2191 void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,2192                            uint32_t class_def_method_index) {2193   Runtime* const runtime = Runtime::Current();2194   if (runtime->IsAotCompiler()) {2195     // 以下代码只适用于非compiler的runtime2196     return;2197   }2198   // method不应该已经被linked了。2199   DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);2200   if (oat_class != nullptr) {2201     // 每种method应当至少从oat_method得到一个invoke stub2202     // 非抽象method也会获得他们的code pointers2203     const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);2204     oat_method.LinkMethod(method);2205   }2206 2207   // Install entry point from interpreter.2208   bool enter_interpreter = NeedsInterpreter(method, method->GetEntryPointFromQuickCompiledCode());2209   if (enter_interpreter && !method->IsNative()) {2210     method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);2211   } else {2212     method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);2213   }2214 2215   if (method->IsAbstract()) {2216     method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());2217     return;2218   }2219 2220   if (method->IsStatic() && !method->IsConstructor()) {2221     // 对于静态method,除了class initializer外,install the trampoline。2222     // 在初始化类后,它将会被ClassLinker::FixupStaticTrampolines用恰当的entry point取代。2223     2224     method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());2225   } else if (enter_interpreter) {2226     if (!method->IsNative()) {2227       // Set entry point from compiled code if there's no code or in interpreter only mode.2228       method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());2229     } else {2230       method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());2231     }2232   }2233 2234   if (method->IsNative()) {2235     // Unregistering restores the dlsym lookup stub.2236     method->UnregisterNative();2237 2238     if (enter_interpreter) {2239       // We have a native method here without code. Then it should have either the generic JNI2240       // trampoline as entrypoint (non-static), or the resolution trampoline (static).2241       // TODO: this doesn't handle all the cases where trampolines may be installed.2242       const void* entry_point = method->GetEntryPointFromQuickCompiledCode();2243       DCHECK(IsQuickGenericJniStub(entry_point) || IsQuickResolutionStub(entry_point));2244     }2245   }2246 }

这个函数定义在文件art/runtime/class_linker.cc中。
函数LinkCode的详细解释可以参考前面Android运行时ART加载类和方法的过程分析一文,这里我们只对结论进行总结,以及对结论进行进一步的分析:
1. ART运行时有两种执行方法:解释执行模式和本地机器指令执行模式。默认是本地机器指令执行模式,但是在启动ART运行时时可以通过-Xint选项指定为解释执行模式。
2. 即使是在本地机器指令模式中,也有类方法可能需要以解释模式执行。反之亦然。解释执行的类方法通过函数artInterpreterToCompiledCodeBridge的返回值调用本地机器指令执行的类方法;本地机器指令执行的类方法通过函数GetQuickToInterpreterBridge的返回值调用解释执行的类方法;解释执行的类方法通过函数artInterpreterToInterpreterBridge 的返回值解释执行的类方法。
3. 在解释执行模式下,除了JNI方法和动态Proxy方法,其余所有的方法均通过解释器执行,它们的入口点设置为函数GetQuickToInterpreterBridge的返回值。
4. 抽象方法不能执行,它必须要由子类实现,因此会将抽象方法的入口点设置为函数GetQuickToInterpreterBridge的返回值,目的检测是否在本地机器指令中调用了抽象方法。如果调用了,上述入口点就会抛出一个异常。
5. 静态类方法的执行模式延迟至类初始化确定。在类初始化之前,它们的入口点由函数GetQuickResolutionStub的返回值代理。

    接下来,我们就着重分析**artInterpreterToCompiledCodeBridge、GetQuickToInterpreterBridge、artInterpreterToInterpreterBridge和GetQuickResolutionStub**这4个函数以及它们所返回的函数的实现,以便可以更好地理解上述5个结论。    函数artInterpreterToCompiledCodeBridge用来在解释器中调用以本地机器指令执行的函数,它的实现如下所示:    
 28 extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, const DexFile::CodeItem* code_item, 29                                                    ShadowFrame* shadow_frame, JValue* result) { 30   ArtMethod* method = shadow_frame->GetMethod(); 31   // Ensure static methods are initialized. 32   if (method->IsStatic()) { 33     mirror::Class* declaringClass = method->GetDeclaringClass(); 34     if (UNLIKELY(!declaringClass->IsInitialized())) { 35       self->PushShadowFrame(shadow_frame); 36       StackHandleScope<1> hs(self); 37       Handle<mirror::Class> h_class(hs.NewHandle(declaringClass)); 38       if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, 39                                                                             true))) { 40         self->PopShadowFrame(); 41         DCHECK(self->IsExceptionPending()); 42         return; 43       } 44       self->PopShadowFrame(); 45       CHECK(h_class->IsInitializing()); 46       // Reload from shadow frame in case the method moved, this is faster than adding a handle. 47       method = shadow_frame->GetMethod(); 48     } 49   } 50   uint16_t arg_offset = (code_item == nullptr) ? 0 : code_item->registers_size_ - code_item->ins_size_; 51   method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset), 52                  (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t), 53                  result, method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty()); 54 }

这个函数定义在文件art/runtime/entrypoints/interpreter/interpreter_entrypoints.cc中。
被调用的类方法通过一个ArtMethod对象来描述,并且可以在调用栈帧shadow_frame中获得。获得了用来描述被调用方法的ArtMehtod对象之后,就可以调用它的成员函数Invoke来对它进行执行。后面我们就会看到,ArtMethod类的成员函数Invoke会找到类方法的本地机器指令来执行。
在调用类方法的本地机器指令的时候,从解释器调用栈获取的传入参数根据ART运行时使用的是Quick后端还是Portable后端来生成本地机器指令有所不同。不过最终都会ArtMethod类的成员函数Invoke来执行被调用类方法的本地机器指令。
函数GetQuickToInterpreterBridge用来返回一个函数指针,这个函数指针指向的函数用来从以本地机器指令执行的类方法中调用以解释执行的类方法,它的实现如下所示:

 37 // Return the address of quick stub code for bridging from quick code to the interpreter. 38 extern "C" void art_quick_to_interpreter_bridge(ArtMethod*); 39 static inline const void* GetQuickToInterpreterBridge() { 40   return reinterpret_cast<const void*>(art_quick_to_interpreter_bridge); 41 } 

这个函数定义在runtime/entrypoints/runtime_asm_entrypoints.h中。
而里面用到的art_quick_to_interpreter_bridge函数与架构相关,是汇编实现的,用来从本地机器指令进入到解释器的。

1638 /*1639  * Called to bridge from the quick to interpreter ABI. On entry the arguments match those1640  * of a quick call:1641  * x0 = method being called/to bridge to.1642  * x1..x7, d0..d7 = arguments to that method.1643  */1644 ENTRY art_quick_to_interpreter_bridge1645     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME   // Set up frame and save arguments.1646 1647     //  x0 will contain mirror::ArtMethod* method.1648     mov x1, xSELF                          // How to get Thread::Current() ???1649     mov x2, sp1650 1651     // uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Thread* self,1652     //                                      mirror::ArtMethod** sp)1653     bl   artQuickToInterpreterBridge1654 1655     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME  // TODO: no need to restore arguments in this case.1656 1657     fmov d0, x01658 1659     RETURN_OR_DELIVER_PENDING_EXCEPTION1660 END art_quick_to_interpreter_bridge

这个实现在runtime/arch/arm64/quick_entrypoints_arm64.S中
很明显,函数art_quick_to_interpreter_bridge通过调用另外一个函数artQuickToInterpreterBridge从本地机器指令进入到解释器中去。
函数artQuickToInterpreterBridge的实现如下所示:

 598 extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, ArtMethod** sp) 599     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 600   // Ensure we don't get thread suspension until the object arguments are safely in the shadow 601   // frame. 602   ScopedQuickEntrypointChecks sqec(self); 603  604   if (method->IsAbstract()) { 605     ThrowAbstractMethodError(method); 606     return 0; 607   } else { 608     DCHECK(!method->IsNative()) << PrettyMethod(method); 609     const char* old_cause = self->StartAssertNoThreadSuspension( 610         "Building interpreter shadow frame"); 611     const DexFile::CodeItem* code_item = method->GetCodeItem(); 612     DCHECK(code_item != nullptr) << PrettyMethod(method); 613     uint16_t num_regs = code_item->registers_size_; 614     void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); 615     // No last shadow coming from quick. 616     ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, nullptr, method, 0, memory)); 617     size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_; 618     uint32_t shorty_len = 0; 619     auto* non_proxy_method = method->GetInterfaceMethodIfProxy(sizeof(void*)); 620     const char* shorty = non_proxy_method->GetShorty(&shorty_len); 621     BuildQuickShadowFrameVisitor shadow_frame_builder(sp, method->IsStatic(), shorty, shorty_len, 622                                                       shadow_frame, first_arg_reg); 623     shadow_frame_builder.VisitArguments(); 624     const bool needs_initialization = 625         method->IsStatic() && !method->GetDeclaringClass()->IsInitialized(); 626     // Push a transition back into managed code onto the linked list in thread. 627     ManagedStack fragment; 628     self->PushManagedStackFragment(&fragment); 629     self->PushShadowFrame(shadow_frame); 630     self->EndAssertNoThreadSuspension(old_cause); 631   632     if (needs_initialization) { 633       // Ensure static method's class is initialized. 634       StackHandleScope<1> hs(self); 635       Handle<mirror::Class> h_class(hs.NewHandle(shadow_frame->GetMethod()->GetDeclaringClass())); 636       if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) { 637         DCHECK(Thread::Current()->IsExceptionPending()) << PrettyMethod(shadow_frame->GetMethod()); 638         self->PopManagedStackFragment(fragment); 639         return 0; 640       } 641     } 642     JValue result = interpreter::EnterInterpreterFromEntryPoint(self, code_item, shadow_frame); 643     // Pop transition. 644     self->PopManagedStackFragment(fragment); 645  646     // Request a stack deoptimization if needed 647     ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp); 648     if (UNLIKELY(Dbg::IsForcedInterpreterNeededForUpcall(self, caller))) { 649       self->SetException(Thread::GetDeoptimizationException()); 650       self->SetDeoptimizationReturnValue(result, shorty[0] == 'L'); 651     } 652  653     // No need to restore the args since the method has already been run by the interpreter. 654     return result.GetJ(); 655   } 656 }

这个函数定义在文件art/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc中。
函数artQuickToInterpreterBridge的作用实际上就是找到被调用类方法method的DEX字节码code_ item,然后根据调用传入的参数构造一个解释器调用栈帧shadow_frame,最后就可以通过函数interpreter::EnterInterpreterFromEntryPoint进入到解释器去执行了。
既然已经知道了要执行的类方法的DEX字节码,以及已经构造好了要执行的类方法的调用栈帧,我们就不难理解解释器是如何执行该类方法了,具体可以参考一下Dalvik虚拟机的运行过程分析这篇文章描述的Dalvik虚拟机解释器的实现。
如果要执行的类方法method是一个静态方法,那么我们就需要确保它的声明类是已经初始化过了的。如果还没有初始化过,那么就需要调用ClassLinker类的成员函数EnsureInitialized来对它进行初始化。
函数artInterpreterToInterpreterBridge用来从解释执行的函数调用到另外一个也是解释执行的函数,它的实现如下所示:

443 extern "C" void artInterpreterToInterpreterBridge(Thread* self, const DexFile::CodeItem* code_item,444                                                   ShadowFrame* shadow_frame, JValue* result) {445   bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();446   if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {447     ThrowStackOverflowError(self);448     return;449   }450 451   self->PushShadowFrame(shadow_frame);452   // Ensure static methods are initialized.453   const bool is_static = shadow_frame->GetMethod()->IsStatic();454   if (is_static) {455     mirror::Class* declaring_class = shadow_frame->GetMethod()->GetDeclaringClass();456     if (UNLIKELY(!declaring_class->IsInitialized())) {457       StackHandleScope<1> hs(self);458       HandleWrapper<Class> h_declaring_class(hs.NewHandleWrapper(&declaring_class));459       if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(460           self, h_declaring_class, true, true))) {461         DCHECK(self->IsExceptionPending());462         self->PopShadowFrame();463         return;464       }465       CHECK(h_declaring_class->IsInitializing());466     }467   }468 469   if (LIKELY(!shadow_frame->GetMethod()->IsNative())) {470     result->SetJ(Execute(self, code_item, *shadow_frame, JValue()).GetJ());471   } else {472     // We don't expect to be asked to interpret native code (which is entered via a JNI compiler473     // generated stub) except during testing and image writing.474     CHECK(!Runtime::Current()->IsStarted());475     Object* receiver = is_static ? nullptr : shadow_frame->GetVRegReference(0);476     uint32_t* args = shadow_frame->GetVRegArgs(is_static ? 0 : 1);477     UnstartedRuntime::Jni(self, shadow_frame->GetMethod(), receiver, args, result);478   }479 480   self->PopShadowFrame();481 }

这个函数定义在文件art/runtime/interpreter/interpreter.cc中。
对比函数artInterpreterToInterpreterBridge和artQuickToInterpreterBridge的实现就可以看出,虽然都是要跳入到解释器去执行一个被调用类方法,但是两者的实现是不一样的。前者由于调用方法本来就是在解释器中执行的,因此,调用被调用类方法所需要的解释器栈帧实际上已经准备就绪,并且被调用方法的DEX字节码也已经知晓,因此这时候就可以直接调用另外一个函数Execute来继续在解释器中执行。
同样,如果被调用的类方法是一个静态方法,并且它的声明类还没有被初始化,那么就需要调用ClassLinker类的成员函数EnsureInitialized来确保它的声明类是已经初始化好了的。
如果被调用的类方法是一个JNI方法,那么此种情况在ART运行时已经启动之后不允许的(ART运行时启动之前允许,但是只是测试ART运行时时才会用到),因为JNI方法在解释器中有自己的调用方式,而函数函数artInterpreterToInterpreterBridge仅仅是用于调用非JNI方法,因此这时候就会调用另外一个函数UnstartedRuntime::Jni记录和抛出错误。

函数GetQuickResolutionStub用来获得一个延迟链接类方法的函数。这个延迟链接类方法的函数用作那些在类加载时还没有链接好的方法的调用入口点,也就是还没有确定调用入口的类方法。对于已经链接好的类方法来说,无论它是解释执行,还是本地机器指令执行,相应的调用入口都是已经通过ArtMehtod类的成员函数SetEntryPointFromCompiledCode和SetEntryPointFromInterpreter设置好了的。如上所述,这类典型的类方法就是静态方法,它们需要等到类初始化的时候才会进行链接。

 55 // Return the address of quick stub code for resolving a method at first call. 56 extern "C" void art_quick_resolution_trampoline(ArtMethod*); 57 static inline const void* GetQuickResolutionStub() { 58   return reinterpret_cast<const void*>(art_quick_resolution_trampoline); 59 }

这个函数定义在runtime/entrypoints/runtime_asm_entrypoints.h中。
art_quick_resolution_trampoline同样是与架构相关,用汇编语言实现,返回在first call时用来resolve method的quick stub code的地址。

1477 ENTRY art_quick_resolution_trampoline1478     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME1479     mov x2, xSELF1480     mov x3, sp1481     bl artQuickResolutionTrampoline  // (called, receiver, Thread*, SP)1482     cbz x0, 1f1483     mov xIP0, x0            // Remember returned code pointer in xIP0.1484     ldr x0, [sp, #0]        // artQuickResolutionTrampoline puts called method in *SP.1485     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME1486     br xIP01487 1:1488     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME1489     DELIVER_PENDING_EXCEPTION1490 END art_quick_resolution_trampoline

这个函数在runtime/arch/arm64/quick_entrypoints_arm64.S中实现。函数art_quick_resolution_trampoline首先是调用另外一个函数artQuickResolutionTrampoline来获得真正要调用的函数的地址,并且通过bl指令跳到该地址去执行。函数artQuickResolutionTrampoline的作用就是用来延迟链接类方法的,也就是等到该类方法被调用时才会对它进行解析链接,确定真正要调用的函数。
函数artQuickResolutionTrampoline的实现如下所示:

 813 // Lazily resolve a method for quick. Called by stub code. 814 extern "C" const void* artQuickResolutionTrampoline( 815     ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp) 816     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 817   ScopedQuickEntrypointChecks sqec(self); 818   // Start new JNI local reference state 819   JNIEnvExt* env = self->GetJniEnv(); 820   ScopedObjectAccessUnchecked soa(env); 821   ScopedJniEnvLocalRefState env_state(env);    ... 824   // Compute details about the called method (avoid GCs) 825   ClassLinker* linker = Runtime::Current()->GetClassLinker(); 826   ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp); 827   InvokeType invoke_type; 828   MethodReference called_method(nullptr, 0); 829   const bool called_method_known_on_entry = !called->IsRuntimeMethod(); 830   if (!called_method_known_on_entry) { 831     uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp)); 832     const DexFile::CodeItem* code; 833     called_method.dex_file = caller->GetDexFile(); 834     code = caller->GetCodeItem(); 835     CHECK_LT(dex_pc, code->insns_size_in_code_units_); 836     const Instruction* instr = Instruction::At(&code->insns_[dex_pc]); 837     Instruction::Code instr_code = instr->Opcode(); 838     bool is_range; 839     switch (instr_code) { 840       case Instruction::INVOKE_DIRECT: 841         invoke_type = kDirect; 842         is_range = false; 843         break; 844       case Instruction::INVOKE_DIRECT_RANGE: 845         invoke_type = kDirect; 846         is_range = true; 847         break; 848       case Instruction::INVOKE_STATIC: 849         invoke_type = kStatic; 850         is_range = false; 851         break; 852       case Instruction::INVOKE_STATIC_RANGE: 853         invoke_type = kStatic; 854         is_range = true; 855         break; 856       case Instruction::INVOKE_SUPER: 857         invoke_type = kSuper; 858         is_range = false; 859         break; 860       case Instruction::INVOKE_SUPER_RANGE: 861         invoke_type = kSuper; 862         is_range = true; 863         break; 864       case Instruction::INVOKE_VIRTUAL: 865         invoke_type = kVirtual; 866         is_range = false; 867         break; 868       case Instruction::INVOKE_VIRTUAL_RANGE: 869         invoke_type = kVirtual; 870         is_range = true; 871         break; 872       case Instruction::INVOKE_INTERFACE: 873         invoke_type = kInterface; 874         is_range = false; 875         break; 876       case Instruction::INVOKE_INTERFACE_RANGE: 877         invoke_type = kInterface; 878         is_range = true; 879         break; 880       default: 881         LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr); 882         UNREACHABLE(); 883     } 884     called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c(); 885   } else { 886     invoke_type = kStatic; 887     called_method.dex_file = called->GetDexFile(); 888     called_method.dex_method_index = called->GetDexMethodIndex(); 889   }    ... 898   // Resolve method filling in dex cache. 899   if (!called_method_known_on_entry) {    ... 905     called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type); 906   } 907   const void* code = nullptr; 908   if (LIKELY(!self->IsExceptionPending())) {     ... 912     if (virtual_or_interface) {  913       // Refine called method based on receiver. 914       CHECK(receiver != nullptr) << invoke_type; 915  916       ArtMethod* orig_called = called; 917       if (invoke_type == kVirtual) { 918         called = receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*)); 919       } else {  920         called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*)); 921       }     ... 962     // Ensure that the called method's class is initialized. 963     StackHandleScope<1> hs(soa.Self()); 964     Handle<mirror::Class> called_class(hs.NewHandle(called->GetDeclaringClass())); 965     linker->EnsureInitialized(soa.Self(), called_class, true, true); 966     if (LIKELY(called_class->IsInitialized())) { 967       if (UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self, called))) { 968         // If we are single-stepping or the called method is deoptimized (by a 969         // breakpoint, for example), then we have to execute the called method 970         // with the interpreter. 971         code = GetQuickToInterpreterBridge(); 972       } else if (UNLIKELY(Dbg::IsForcedInstrumentationNeededForResolution(self, caller))) { 973         // If the caller is deoptimized (by a breakpoint, for example), we have to 974         // continue its execution with interpreter when returning from the called 975         // method. Because we do not want to execute the called method with the 976         // interpreter, we wrap its execution into the instrumentation stubs. 977         // When the called method returns, it will execute the instrumentation 978         // exit hook that will determine the need of the interpreter with a call 979         // to Dbg::IsForcedInterpreterNeededForUpcall and deoptimize the stack if 980         // it is needed. 981         code = GetQuickInstrumentationEntryPoint(); 982       } else { 983         code = called->GetEntryPointFromQuickCompiledCode(); 984       } 985     } else if (called_class->IsInitializing()) { 986       if (UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self, called))) { 987         // If we are single-stepping or the called method is deoptimized (by a 988         // breakpoint, for example), then we have to execute the called method 989         // with the interpreter. 990         code = GetQuickToInterpreterBridge(); 991       } else if (invoke_type == kStatic) { 992         // Class is still initializing, go to oat and grab code (trampoline must be left in place 993         // until class is initialized to stop races between threads). 994         code = linker->GetQuickOatCodeFor(called); 995       } else { 996         // No trampoline for non-static methods. 997         code = called->GetEntryPointFromQuickCompiledCode(); 998       } 999     } else {1000       DCHECK(called_class->IsErroneous());1001     }1002   }    ...1006   // Place called method in callee-save frame to be placed as first argument to quick method.1007   *sp = called;1008 1009   return code;1010 }

这个函数定义在文件art/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc中。
第一个参数called表示被调用的类方法,第二个参数receiver表示被调用的对象,也就是接收消息的对象,第三个参数thread表示当前线程,第四个参数sp指向调用栈顶。通过调用QuickArgumentVisitor类的静态成员函数GetCallingMethod可以在调用栈找到类方法called的调用者,保存在变量caller中。
被调用类方法called有可能是一个运行时方法(Runtime Method)。运行时方法相当是一个替身,它是用来找到被替换的类方法。当调用类方法called是一个运行时方法时,调用它的成员函数IsRuntimeMethod得到的返回值为true,这时候我们就需要找到被替换的类方法。那么问题就来了,怎么找到此时被替换的类方法呢?运行时方法只是一个空壳,没有任何线索可以提供给我们,不过我们却可以在DEX字节码的调用指令中找到一些蜘丝马迹。在DEX字节码中,我们在一个类方法中通过invoke-static/invoke-direct/invoke-interface/invoke-super/invoke-virtual等指令来调用另外一个类方法。在这些调用指令中,有一个寄存器记录了被调用的类方法在DEX文件中的方法索引dex_method_index。有了这个DEX文件方法索引之后,我们就可以在相应的DEX文件找到被替换的类方法了。现在第二个问题又来了,我们要在哪一个DEX文件查找被替换的类方法呢?函数artQuickResolutionTrampoline适用的是调用方法caller和被调用方法called均是位于同一个DEX文件的情况。因此,我们可以通过调用方法caller来得到要查找的DEX文件dex_file。有了上述两个重要的信息之后,函数artQuickResolutionTrampoline接下来就可以调用ClassLinker类的成员函数ResolveMethod来查找被替换的类方法了,并且继续保存在参数called中。另一方面,如果被调用类方法called不是运行时方法,那么情况就简单多了,因为此时called描述的便是要调用的类方法。
经过上面的处理之后,参数called指向的ArtMethod对象还不一定是最终要调用的类方法。这是因为当前发生的可能是一个虚函数调用或者接口调用。在上述两种情况下,我们需要通过接收消息的对象receiver来确定真正被调用的类方法。为了完成这个任务,我们首先通过调用Object类的成员函数GetClass获得接收消息的对象receiver的类对象,接着再通过调用过Class类的成员函数FindVirtualMethodForVirtual或者FindVirtualMethodForInterface来获得真正要被调用的类方法。前者针对的是虚函数调用,而后者针对的是接口调用。
最终我们得到的真正被调用的类方法仍然是保存在参数called中。这时候事情还没完,因为此时被调用的类方法所属的类可能还没有初始化好。因此,在继续下一步操作之前,我们需要调用ClassLinker类的成员函数EnsureInitialized来确保存被调用类方法called所属的类已经初始好了。在调用ClassLinker类的成员函数EnsureInitialized的时候,如果被调用类方法called所属的类还没有初始化,那么就会对它进行初始化,不过不等它初始化完成就返回了。因此,这时候就可能会出现两种情况。
第一种情况是被调用类方法called所属的类已经初始好了。这时候我们就可以直接调用它的成员函数GetEntryPointFromCompiledCode来获得它的本地机器指令或者DEX字节码,取决于它是以本地机器指令方式执行还是通过解释器来执行。
第二种情况是被调用方法called所属的类正在初始化中。这时候需要区分静态和非静态调用两种情况。在进一步解释之前,我们需要明确,类加载和类初始化是两个不同的操作。类加载的过程并不一定会伴随着类的初始化。此时我们唯一确定的是被调用方法called所属的类已经被加载(否则它的类方法无法被调用)。又从前面Android运行时ART加载类和方法的过程分析这篇文章可以知道,当一个类被加载时,除了它的静态成员函数,其余所有的成员函数均已加载完毕。这意味着我们可以直接调用ArtMethod类的成员函数GetEntryPointFromCompiledCode来获得被调用方法called的本地机器指令或者DEX字节码。对于静态成员函数的情况,我们就唯有到DEX文件去查找到被调用方法called的本地机器指令了。这是通过调用ClassLinker类的成员函数GetOatCodeFor来实现的。当然,如果该静态成员函数不存在本地机器指令,那么ClassLinker类的成员函数GetOatCodeFor返回的是进入解释器的入口函数地址。这样我们就可以通过解释器来执行该静态成员函数了。
最后,函数artQuickResolutionTrampoline将获得的真正被调用的类方法的执行入口地址code返回给前一个函数,即art_quick_resolution_trampoline,以便后者可以通过bl跳过去执行。函数artQuickResolutionTrampoline在返回之前,同时还会将此时栈顶的内容设置为真正被调用的类方法对象,以便真正被调用的类方法在运行时,可以获得正确的调用栈帧。
到这里,函数artQuickResolutionTrampoline的实现就分析完成了。不过对于上面提到的运行时方法,我们还需要继续解释。只有了理解了运行时方法的作用之后,我们才能真正理解函数artQuickResolutionTrampoline的作用。
运行时方法与另一个称为Dex Cache的机制有关。在ART运行时中,每一个DEX文件都有一个关联的Dex Cache,用来缓存对应的DEX文件中已经被解析过的信息,例如类方法和类属性等。这个Dex Cache使用类DexCache来描述,它的定义如下所示:

 38 // C++ mirror of java.lang.DexCache. 39 class MANAGED DexCache FINAL : public Object { 40  public:     ...150  private:151   HeapReference<Object> dex_;152   HeapReference<String> location_;153   // Either an int array or long array based on runtime ISA since these arrays hold pointers.154   HeapReference<PointerArray> resolved_fields_;155   HeapReference<PointerArray> resolved_methods_;156   HeapReference<ObjectArray<Class>> resolved_types_;157   HeapReference<ObjectArray<String>> strings_;158   uint64_t dex_file_;159 160   friend struct art::DexCacheOffsets;  // for verifying offset information161   DISALLOW_IMPLICIT_CONSTRUCTORS(DexCache);162 };

这个类定义在文件rt/runtime/mirror/dex_cache.h中。
这里我们只关注Dex Cache中的类方法,它通过成员变量resolved_methods_来描述。
在ART运行时中,每当一个类被加载时,ART运行时都会检查该类所属的DEX文件是否已经关联有一个Dex Cache。如果还没有关联,那么就会创建一个Dex Cache,并且建立好关联关系。以Java层的DexFile类为例,当我们通过调用它的成员函数loadClass来加载一个类的时候,最终会调用到C++层的JNI函数DexFile_defineClassNative来执行真正的加载操作。
函数DexFile_defineClassNative的实现如下所示:

218 static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,219                                         jobject cookie) {220   std::unique_ptr<std::vector<const DexFile*>> dex_files = ConvertJavaArrayToNative(env, cookie);    ...227   ScopedUtfChars class_name(env, javaName);    ...232   const std::string descriptor(DotToDescriptor(class_name.c_str()));233   const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));234   for (auto& dex_file : *dex_files) {235     const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);236     if (dex_class_def != nullptr) {237       ScopedObjectAccess soa(env);238       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();239       class_linker->RegisterDexFile(*dex_file);240       StackHandleScope<1> hs(soa.Self());241       Handle<mirror::ClassLoader> class_loader(242           hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));243       mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,244                                                         class_loader, *dex_file, *dex_class_def);245       if (result != nullptr) {246         VLOG(class_linker) << "DexFile_defineClassNative returning " << result247                            << " for " << class_name.c_str();248         return soa.AddLocalReference<jclass>(result);249       }250     }251   }252   VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();253   return nullptr;254 }

这个函数定义在文件art/runtime/native/dalvik_system_DexFile.cc中。
参数cookie指向之前已经打开了的DEX文件,因此这里首先将它转换为一个DexFile指针dex_file。这个DEX文件是包含在OAT文件里面的,它们的打开过程可以参考Android运行时ART加载OAT文件的过程分析一文。得到了之前打开的DEX文件之后,接下来就调用ClassLinker类的成员函数RegisterDexFile将它注册到ART运行时中去,以便以后可以查询使用。最后再通过ClassLinker类的成员函数DefineClass来加载参数javaName指定的类。
类的加载过程可以参考前面Android运行时ART加载类和方法的过程分析一文,接下来我们主要关注ClassLinker类的成员函数RegisterDexFile的实现,如下所示:

2475 void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file,2476                                         Handle<mirror::DexCache> dex_cache) {2477   dex_lock_.AssertExclusiveHeld(Thread::Current());2478   CHECK(dex_cache.Get() != nullptr) << dex_file.GetLocation();2479   CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation()))2480       << dex_cache->GetLocation()->ToModifiedUtf8() << " " << dex_file.GetLocation();2481   dex_caches_.push_back(GcRoot<mirror::DexCache>(dex_cache.Get()));2482   dex_cache->SetDexFile(&dex_file);2483   if (log_new_dex_caches_roots_) {2484     // TODO: This is not safe if we can remove dex caches.2485     new_dex_cache_roots_.push_back(dex_caches_.size() - 1);2486   }2487 }2489 void ClassLinker::RegisterDexFile(const DexFile& dex_file) {2490   Thread* self = Thread::Current();2491   {  2492     ReaderMutexLock mu(self, dex_lock_);2493     if (IsDexFileRegisteredLocked(dex_file)) {2494       return;2495     }2496   }    2497   // Don't alloc while holding the lock, since allocation may need to2498   // suspend all threads and another thread may need the dex_lock_ to2499   // get to a suspend point.2500   StackHandleScope<1> hs(self);2501   Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));2502   CHECK(dex_cache.Get() != nullptr) << "Failed to allocate dex cache for "2503                                     << dex_file.GetLocation();2504   {2505     WriterMutexLock mu(self, dex_lock_);2506     if (IsDexFileRegisteredLocked(dex_file)) {2507       return;2508     } 2509     RegisterDexFileLocked(dex_file, dex_cache);2510   } 2511 } 

这个函数定义在文件art/runtime/class_linker.cc中。
ClassLinker类的成员函数RegisterDexFile首先将调用另外一个成员函数IsDexFileRegisteredLocked检查参数dex_file指定的DEX文件是否已经注册过了。如果已经注册过了,那么就什么也不用做。否则的话,就调用ClassLinker类的成员函数AllocDexCache为其分配一个Dex Cache,并且调用ClassLinker类的成员函数RegisterDexFileLocked执行真正的注册工作。
从上面的分析就可以看出,注册DEX文件实际上就是创建一个与之关联的Dex Cache,并且将该Dex Cache保存在ClassLinker类的成员变量dex_caches_所描述的一个向量中。不过,这里我们关注的是注册过程中所创建的Dex Cache。因此,接下来我们继续分析ClassLinker类的成员函数AllocDexCache的实现,如下所示:

1467 mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) {1468   StackHandleScope<6> hs(self);1469   auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(1470       GetClassRoot(kJavaLangDexCache)->AllocObject(self))));    ...1475   auto location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str())));    ...            1480   auto strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds())));    ...1485   auto types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds())));    ...1490   auto methods(hs.NewHandle(AllocPointerArray(self, dex_file.NumMethodIds())));    ...1495   auto fields(hs.NewHandle(AllocPointerArray(self, dex_file.NumFieldIds())));    ...1500   dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(),1501                   fields.Get(), image_pointer_size_);1502   return dex_cache.Get();1503 }

这个函数定义在文件art/runtime/class_linker.cc中。
我们要创建的Dex Cache使用java.lang.DexCache类来描述。java.lang.DexCache类对象保存在ART运行时的Image空间中,我们可以通过ClassLinker类的成员函数GetClassRoot来获得的。获得了用来描述java.lang.DexCache类的类对象之后,我们就可以调用Heap类的成员函数AllocObject在ART运行堆上分配一个DexCache对象了。关于ART运行时的Image空间和堆,我们接下来的文章中将会详细分析。
Dex Cache的作用是用来缓存包含在一个DEX文件里面的类型(Type)、方法(Method)、域(Field)、字符串(String)和静态储存区(Static Storage)等信息。因此,我们需要为上述提到的信息分配储存空间。例如,对于类方法来说,我们需要创建一个ArtMethod对象指针数组,其中每一个ArtMethod对象都用来描述DEX文件里面的一个类方法。有了这些储存空间之后,最后就可以调用DexCache类的成员函数Init对刚才创建的Dex Cache进行初始化了。
DexCache类的成员函数Init的实现如下所示:

 34 void DexCache::Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings, 35                     ObjectArray<Class>* resolved_types, PointerArray* resolved_methods, 36                     PointerArray* resolved_fields, size_t pointer_size) { 37   CHECK(dex_file != nullptr); 38   CHECK(location != nullptr); 39   CHECK(strings != nullptr); 40   CHECK(resolved_types != nullptr); 41   CHECK(resolved_methods != nullptr); 42   CHECK(resolved_fields != nullptr); 43  44   SetDexFile(dex_file); 45   SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location); 46   SetFieldObject<false>(StringsOffset(), strings); 47   SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields); 48   SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types); 49   SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods);//重点 50  51   Runtime* const runtime = Runtime::Current(); 52   if (runtime->HasResolutionMethod()) { 53     // Initialize the resolve methods array to contain trampolines for resolution. 54     Fixup(runtime->GetResolutionMethod(), pointer_size); 55   } 56 }

这个函数定义在文件art/runtime/mirror/dex_cache.cc中。
我们重点关注Dex Cache中的类方法对象数组的初始化。前面提到,DexCache类有一个类型为ObjectArray的resolved_ methods_ 指针数组。我们通过DexCache类的成员函数ResolvedMethodsOffset可以知道它在DexCache类中的偏移值。有了这个偏移值之后,就可以调用父类Object的成员函数SetFieldObject来将参数resolved_ methods描述的ArtMethod对象指针数组设置到当前正在初始化的DexCache对象的成员变量resolved_methods_去了。
接下来重点就来了,DexCache类的成员变量resolved_methods_指向的ArtMethod对象指针数组中的每一个ArtMethod指针都会指向同一个Resolution Method。这个Resolution Method可以通过Runtime类的成员函数GetResolutionMethod获得,它的实现如下所示:

 53 inline ArtMethod* Runtime::GetResolutionMethod() { 54   CHECK(HasResolutionMethod()); 55   return resolution_method_; 56 } //runtime/runtime.h中583   ArtMethod* resolution_method_;

该函数的实现在runtime/runtime-inl.h中。
Runtime类的成员函数GetResolutionMethod返回的是成员变量resolution_method_指向的一个ArtMethod对象。
那么Runtime类的成员变量resolution_method_是什么时候初始化的呢?是在ART运行时的Image空间初始化过程中初始化的。在前面Android运行时ART加载OAT文件的过程分析一篇文章中,我们提到,ART运行时的Image空间创建完成之后,会调用ImageSpace类的成员函数Init来对它进行初始化。
Resolution Method是通过调用Runtime类的成员函数CreateResolutionMethod来创建的,如下所示:

//art/runtime/runtime.cc1461 ArtMethod* Runtime::CreateResolutionMethod() {1462   auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();1463   // When compiling, the code pointer will get set later when the image is loaded.1464   if (IsAotCompiler()) {1465     size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);//依据架构不同,对于ARM64而言,为8.1466     method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);1467   } else {1468     method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());1469   }1470   return method;1471 }//runtime/class_linker.cc5899 ArtMethod* ClassLinker::CreateRuntimeMethod() {5900   ArtMethod* method = AllocArtMethodArray(Thread::Current(), 1);5901   CHECK(method != nullptr);5902   method->SetDexMethodIndex(DexFile::kDexNoIndex);5903   CHECK(method->IsRuntimeMethod());5904   return method;5905 } 

从Runtime类的成员函数CreateResolutionMethod的实现就可以看出,ART运行时的Resolution Method有以下两个特点:
1. 它的Dex Method Index为DexFile::kDexNoIndex,这是因为它不代表任何的类方法。
2. 由于上述原因,它没有相应的本地机器指令,因此它不能执行。
回想我们前面分析的函数artQuickResolutionTrampoline,它通过ArtMethod类的成员函数IsRuntimeMethod来判断一个ArtMethod对象是否是一个运行时方法。ArtMethod类的成员函数IsRuntimeMethod的实现如下所示:

//runtime/art_method-inl.h270 inline bool ArtMethod::IsRuntimeMethod() {271   return dex_method_index_ == DexFile::kDexNoIndex;272 }

如果一个ArtMethod的Dex Method Index等于DexFile::kDexNoIndex,那么它就被认为是运行时方法。当然,Resoultion Method只是运行方法的其中一种。其中类型的运行时方法我们后面分析ART运行时的Image空间的文章时再讲解。
如前面所述,函数artQuickResolutionTrampoline一旦发现一个接着要调用的类方法是一个运行时方法时,就会调用ClassLinker类的成员函数ResolveMethod来对其进行解析,也就是找到真正要被调用的类方法。
ClassLinker类的成员函数ResolveMethod的实现如下所示:

///runtime/class_linker-inl.h116 inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, uint32_t method_idx,117                                              ArtMethod* referrer, InvokeType type) {118   ArtMethod* resolved_method = GetResolvedMethod(method_idx, referrer);119   if (UNLIKELY(resolved_method == nullptr)) {120     mirror::Class* declaring_class = referrer->GetDeclaringClass();121     StackHandleScope<2> hs(self);122     Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));123     Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));124     const DexFile* dex_file = h_dex_cache->GetDexFile();125     resolved_method = ResolveMethod(*dex_file, method_idx, h_dex_cache, h_class_loader, referrer,126                                     type);127   }128   // Note: We cannot check here to see whether we added the method to the cache. It129   //       might be an erroneous class, which results in it being hidden from us.130   return resolved_method;131 }

这个函数定义在文件art/runtime/class_linker-inl.h中。
参数method_idx描述的是接下来将要被调用类方法在DEX文件的索引。注意,每一个类方法在宿主类中有一个索引,在对应的DEX文件中也有一个索引。这两个索引是不一样的,根据前面Android运行时ART加载类和方法的过程分析一文,前一个索引用来查找一个类方法的本地机器指令。而后面一个索引,自然的就是用来DEX文件中找到对应的类方法描述信息了。这意味着一旦知道一个类方法在DEX文件的索引,那么就可以在对应的DEX文件中对该类方法进行解析了。一旦解析完成,自然就可以知道接下来要被调用的类方法是什么了。
参数referrer指向的ArtMethod对象描述的是调用者(类方法)。每一个类方法都关联有一个ArtMethod对象指针数组,这个ArtMethod对象指针数组实际上就是我们在前面提到的Dex Cache中的ArtMethod对象指针数组。同时,每一个类对象(Class)也关联有一个Dex Cache。这个Dex Cache实际上就是与包含该类的DEX文件相关联的Dex Cache。为了搞清楚上述关系,我们回顾一下前面Android运行时ART加载类和方法的过程分析一文提到的ClassLinker类的两个成员函数DefineClass和LoadMethod。
在ClassLinker类的成员函数DefineClass中,会给每一个加载的类关联一个Dex Cache,如下所示:

//runtime/class_linker.cc1808 mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,1809                                         Handle<mirror::ClassLoader> class_loader,1810                                         const DexFile& dex_file,1811                                         const DexFile::ClassDef& dex_class_def) {1812   StackHandleScope<3> hs(self);1813   auto klass = hs.NewHandle<mirror::Class>(nullptr);    ...1842   klass->SetDexCache(FindDexCache(dex_file));    ...1864   // Load the fields and other things after we are inserted in the table. This is so that we don't1865   // end up allocating unfree-able linear alloc resources and then lose the race condition. The1866   // other reason is that the field roots are only visited from the class table. So we need to be1867   // inserted before we allocate / fill in these fields.1868   LoadClass(self, dex_file, dex_class_def, klass);    ...1931 }

变量klass描述的就是正在加载的类,在对其进行加载之前,首先会调用ClassLinker类的成员函数FindDexCache找到与参数dex_ file描述的DEX文件相关联的Dex Cache。有了这个Dex Cache,就可以将它设置到kclass指向的Class对象中去了。注意,参数dex_file描述的DEX文件就是包含正在加载的类的文件。
在ClassLinker类的成员函数LoadMethod中,会给每一个加载的类方法设置一个DEX文件类方法索引,以及关联一个ArtMethod对象指针数组,如下所示:

// runtime/class_linker.cc2391 void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,2392                              Handle<mirror::Class> klass, ArtMethod* dst) {2393   uint32_t dex_method_idx = it.GetMemberIndex();2394   const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);2395   const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);2396 2397   ScopedAssertNoThreadSuspension ants(self, "LoadMethod");2398   dst->SetDexMethodIndex(dex_method_idx);2399   dst->SetDeclaringClass(klass.Get());2400   dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());2401 2402   dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());2403   dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());    ...2441   dst->SetAccessFlags(access_flags);2442 }

参数dst描述的便是正在加载的类方法,我们可以通过参数it获得它在DEX文件中的类方法索引,并且将该索引设置到参数dst指向的ArtMethod对象中去。
参数klass描述是正在加载的类方法所属的类,前面我们已经给这个类关联过一个Dex Cache了,因此,只要将重新获得该Dex Cache,并且获得该Dex Cache里面的ArtMethod对象指针数组,那么就可以将ArtMethod对象指针数组设置到正在加载的类方法去了。
从ClassLinker类的两个成员函数DefineClass和LoadMethod的实现就可以看出,同一个DEX文件的所有类关联的Dex Cache都是同一个Dex Cache,并且属于这些类的所有类方法关联的ArtMethod对象指针数组都是该Dex Cache内部的ArtMethod对象指针数组。这个结论对我们理解ClassLinker类的成员函数ResolveMethod的实现很重要。
在ClassLinker类的成员函数ResolveMethod中,我们知道的是调用者以及被调用者在DEX文件中的类方法索引,因此,我们就可以从与调用者关联的ArtMethod对象指针数组中找到接下来真正要被调用的类方法了。
Dex Cache内部的ArtMethod对象指针数组的每一个ArtMethod指针一开始都是指向ART运行时的Resolution Method。但是每当一个类方法第一次被调用的时候,函数artQuickResolutionTrampoline能够根据捕捉到这种情况,并且根据调用者和调用指令的信息,通过ClassLinker类的成员函数ResolveMethod找到接下来真正要被调用的类方法。查找的过程就是解析类方法的过程,这是一个漫长的过程,因为要解析DEX文件。不过一旦接下来要被调用的类方法解析完成,就会创建另外一个ArtMethod对象来描述解析得到的信息,并且将该ArtMethod对象保存在对应的Dex Cache内部的ArtMethod对象指针数组的相应位置去。这样下次该类方法再被调用时,就不用再次解析了。
从上面的分析我们还可以进一步得到以下的结论:
1. 在生成的本地机器指令中,一个类方法调用另外一个类方法并不是直接进行的,而是通过Dex Cache来间接进行的。
2. 通过Dex Cache间接调用类方法,可以做到延时解析类方法,也就是等到类方法第一次被调用时才解析,这样可以避免解析那些永远不会被调用的类方法。
3. 一个类方法只会被解析一次,解析的结果保存在Dex Cache中,因此当该类方法再次被调用时,就可以直接从Dex Cache中获得所需要的信息。
以上就是Dex Cache在ART运行时所起到的作用了,理解这一点对阅读ART运行时的源代码非常重要。
有了以上的知识点之后,接下来我们就可以真正地分析类方法的调用过程了。

阅读全文
0 0
原创粉丝点击