Android KK ART初始化簡析

 

 

Android KK裡引入瞭ART虛擬機作為Dalvik虛擬機的替代,其主要目的是把Bytecode的翻譯優化從運行時提前到安裝時,以空間換時間,從而達到更流暢的用戶體驗。目前,KK中Dalvik仍是默認的虛擬機,但用戶可以在Developer Option中切換到ART虛擬機。坊間傳聞在下一版可能會轉正。Dalvik和ART的實現分別位於libdvm.so和libart.so這兩個庫。兩個可以同時build也可以隻build一個,通過Makefile中的變量PRODUCT_RUNTIMES來控制(https://source.android.com/devices/tech/dalvik/art.html)。ART本質和Dalvik是一樣的,是將Java的Bytecode翻譯成Native code。它的主要的實現代碼在AOSP的art目錄下,另外在libcore/libart/下還有一部分Java層的實現。本文以Zygote中初始化的部分為例簡要分析下ART的大體工作流程。

 

故事從init.rc開始,在init.rc中由這一行表示啟動zygote:

service zygote /system/bin/app_process -Xzygote /system/bin –zygote–start-system-server

init進程根據它執行app_process(frameworks/base/cmds/app_process/app_main.cpp),也就是Zygote瞭。Zygote會初始化AndroidRuntime並調用AndroidRuntime::start()函數:

223        runtime.start(com.android.internal.os.ZygoteInit,
224                startSystemServer ? start-system-server : );

AndroidRuntime::start()函數中,首先會啟動Java虛擬機:

834    /* start the virtual machine */
835    JniInvocation jni_invocation;
836    jni_invocation.Init(NULL);
837    JNIEnv* env;
838    if (startVm(&mJavaVM, &env) != 0) {
839        return;
840    }

這裡JniInvocation會初始化三個重要的Java虛擬機接口函數(聲明在/libnativehelper/include/nativehelper/jni.h):

1097/*
1098 * VM initialization functions.
1099 *
1100 * Note these are the only symbols exported for JNI by the VM.
1101 */
1102jint JNI_GetDefaultJavaVMInitArgs(void*);
1103jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
1104jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);

JniInvocation的Init()函數中,會根據系統屬性persist.sys.dalvik.vm.lib用dlopen()加載libdvm.so或libart.so。這兩個so中都export出瞭JNI_GetDefaultJavaVMInitArgs,JNI_CreateJavaVM和JNI_GetCreatedJavaVMs這三個接口函數。

51#ifdef HAVE_ANDROID_OS
52static const char* kLibrarySystemProperty = persist.sys.dalvik.vm.lib;
53#endif

那這幾個函數是如何export出來的呢?以JNI_CreateJavaVM為例,JniInvocation::Init()中先在庫中找到這個symbol,結果放於JNI_CreateJavaVM_這個成員變量裡:

92  if (!FindSymbol(reinterpret_cast(&JNI_CreateJavaVM_),
93                  JNI_CreateJavaVM)) {
94    return false;
95  }

這個變量裡的函數指針又是通過另一個成員函數來調用的:

107jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
108  return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
109}

但這個成員函數是私有的,外界不可訪問。但註意下面的同名函數被設為JniInvocation類的友元函數,這意味著該函數可以訪問JniInvocation中的private成員。

136extern C jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
137  return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
138}

這樣,JNI_CreateJavaVM就被export出去瞭。其它兩個接口函數也是類似的。

 

接下來,AndroidRuntime::startVm()啟動Java虛擬機。該函數先是處理一堆參數,如執行模式,checkjni等等。大多數參數都是從system property裡讀入,最後存放到mOptions這個結構體裡。這個函數先是初始化一坨虛擬機啟動參數,最後調用JNI_CreateJavaVM()來創建Java虛擬機。JNI_CreateJavaVM()的實現位於/art/runtime/jni_internal.cc。其主要任務是Runtime的初始化和兩個關鍵數據結構-JavaVM和JNIEnv的初始化。前者代表Java虛擬機,每個進程一個;後者代表JNI的調用環境,每個線程一個。官方解釋:https://developer.android.com/training/articles/perf-jni.html

2897  if (!Runtime::Create(options, ignore_unrecognized)) {
2898    return JNI_ERR;
2899  }
2900  Runtime* runtime = Runtime::Current();
2901  bool started = runtime->Start();
2902  if (!started) {
2903    delete Thread::Current()->GetJniEnv();
2904    delete runtime->GetJavaVM();
2905    LOG(WARNING) << CreateJavaVM failed;
2906    return JNI_ERR;
2907  }
2908  *p_env = Thread::Current()->GetJniEnv();
2909  *p_vm = runtime->GetJavaVM();

在JavaVM和JNIEnv這兩個核心數據結構中,最重要的是兩張函數表:

2979const JNIInvokeInterface gJniInvokeInterface = {

在JavaVMExt的構造函數中賦值:

3008  functions = unchecked_functions = &gJniInvokeInterface;

2599const JNINativeInterface gJniNativeInterface = {

在JNIEnvExt構造函數中賦值:

2843  functions = unchecked_functions = &gJniNativeInterface;

 

這幾個類的大體結構如下:

data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20140307/2014030715094620.jpg

在JNI_CreateJavaVM()中,先是調用Create()函數來創建Runtime。Runtime是個單例,創建出來後緊接著調用Init()函數(/art/runtime/runtime.cc)。Init()函數會做很多初始化工作:解析參數,初始化Heap和JavaVMExt結構,線程和信號處理,創建ClassLinker等。

829bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
…
832  UniquePtr options(ParsedOptions::Create(raw_options, ignore_unrecognized));
…
841  Monitor::Init(options->lock_profiling_threshold_, options->hook_is_sensitive_thread_);
…
868  monitor_list_ = new MonitorList;
869  thread_list_ = new ThreadList;
870  intern_table_ = new InternTable;
…
877  heap_ = new gc::Heap(…)
892  BlockSignals();
…
895  java_vm_ = new JavaVMExt(this, options.get());
896
897  Thread::Startup();
902  Thread* self = Thread::Attach(main, false, NULL, false);
…
913  if (GetHeap()->GetContinuousSpaces()[0]->IsImageSpace()) {
914    class_linker_ = ClassLinker::CreateFromImage(intern_table_);
915  } else {
916    CHECK(options->boot_class_path_ != NULL);
917    CHECK_NE(options->boot_class_path_->size(), 0U);
918    class_linker_ = ClassLinker::CreateFromCompiler(*options->boot_class_path_, intern_table_);
919  }

首先,Runtime::ParsedOptions::Create()中會解析參數,把raw_options中的參數放入parsed,如對環境變量BOOTCLASSPATH和CLASSPATH的處理:

324  const char* boot_class_path_string = getenv(BOOTCLASSPATH);
325  if (boot_class_path_string != NULL) {
326    parsed->boot_class_path_string_ = boot_class_path_string;
327  }
328  const char* class_path_string = getenv(CLASSPATH);
329  if (class_path_string != NULL) {
330    parsed->class_path_string_ = class_path_string;
331  }

然後初始化Monitor(相當於mutex+conditional variable,可用於多個線程同步)和線程鏈表等。接著是比較重要的Heap及GC的初始化,

gc::Heap的實現在/art/runtime/gc/heap.cc中:

69Heap::Heap(…)
…
144  live_bitmap_.reset(new accounting::HeapBitmap(this));
145  mark_bitmap_.reset(new accounting::HeapBitmap(this));
…
151      space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name);
…
165  alloc_space_ = space::DlMallocSpace::Create(…)
…
178    large_object_space_ = space::LargeObjectMapSpace::Create(large object space);
…
190  // Allocate the card table.
191  card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity));
…
194  image_mod_union_table_.reset(new accounting::ModUnionTableToZygoteAllocspace(this));
195  CHECK(image_mod_union_table_.get() != NULL) << Failed to create image mod-union table;
196
197  zygote_mod_union_table_.reset(new accounting::ModUnionTableCardCache(this));
198  CHECK(zygote_mod_union_table_.get() != NULL) << Failed to create Zygote mod-union table;
…

其中的ImageSpace::Create()會檢測image文件,如果沒有就調用GenerateImage()來創建。因此log中相應會有:

I/art ( 161): GenerateImage: /system/bin/dex2oat–image=/data/dalvik-cache/system@framework@boot.art –runtime-arg -Xms64m–runtime-arg -Xmx64m –dex-file=/system/framework/core-libart.jar … –oat-file=/data/dalvik-cache/system@framework@boot.oat –base=0x60000000–image-classes-zip=/system/framework/framework…

可以看到它其實是調用瞭dex2oat,該命令把BOOTCLASSPATH裡的包打成image文件,它最後會生成兩個文件boot.art和boot.oat,前者是image文件,後者是elf文件。這個image會被放到創建的Heap中。接下來Heap()為一些數據結構分配空間,創建各種互斥量及初始化GC:

203  // Default mark stack size in bytes.
204  static const size_t default_mark_stack_size = 64 * KB;
205  mark_stack_.reset(accounting::ObjectStack::Create(mark stack, default_mark_stack_size));
206  allocation_stack_.reset(accounting::ObjectStack::Create(allocation stack,
207                                                          max_allocation_stack_size_));
208  live_stack_.reset(accounting::ObjectStack::Create(live stack,
209                                                    max_allocation_stack_size_));
…
214  gc_complete_lock_ = new Mutex(GC complete lock);
215  gc_complete_cond_.reset(new ConditionVariable(GC complete condition variable,
216                                                *gc_complete_lock_));
217
218  // Create the reference queue locks, this is required so for parallel object scanning in the GC.
219  soft_ref_queue_lock_ = new Mutex(Soft reference queue lock);
220  weak_ref_queue_lock_ = new Mutex(Weak reference queue lock);
221  finalizer_ref_queue_lock_ = new Mutex(Finalizer reference queue lock);
222  phantom_ref_queue_lock_ = new Mutex(Phantom reference queue lock);
…
232  // Create our garbage collectors.
233  for (size_t i = 0; i < 2; ++i) {
234    const bool concurrent = i != 0;
235    mark_sweep_collectors_.push_back(new collector::MarkSweep(this, concurrent));
236    mark_sweep_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent));
237    mark_sweep_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent));
238  }

MarkSweep,PartialMarkSweep和StickyMarkSweep都是art::gc::collector::GarbageCollector的繼承類。它和幾個子類應用瞭Template Method模式。GarbageCollector::Run()中實現瞭主要算法,其中調用瞭InitializePhase(),MarkingPhase(),ReclaimPhase()和FinishPhase()等幾個虛函數,這幾個虛函數在MarkSweep等幾個子類中有具體實現。

 

回到Runtime::Init(),下面是ClassLinker的初始化。它主要調用CreateFromImage()函數來實現:

181ClassLinker* ClassLinker::CreateFromImage(InternTable* intern_table) {
182  UniquePtr class_linker(new ClassLinker(intern_table));
183  class_linker->InitFromImage();
184  return class_linker.release();
185}

InitFromImage()函數中,會從Heap中得到image的空間,然後得到dex caches數組,接著把這些dex caches對應的dex file信息註冊到BootClassPath中去。

1017  gc::space::ImageSpace* space = heap->GetImageSpace();
…
1026  mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
1027  mirror::ObjectArray* dex_caches =
1028      dex_caches_object->AsObjectArray();
…
1041  for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
1042    SirtRef dex_cache(self, dex_caches->Get(i));
1043    const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
1044    const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location, NULL);
1045    CHECK(oat_dex_file != NULL) << oat_file.GetLocation() <<   << dex_file_location;
1046    const DexFile* dex_file = oat_dex_file->OpenDexFile();
1047    if (dex_file == NULL) {
1048      LOG(FATAL) << Failed to open dex file  << dex_file_location
1049                 <<  from within oat file  << oat_file.GetLocation();
1050    }
1051
1052    CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
1053
1054    AppendToBootClassPath(*dex_file, dex_cache);
1055  }

AppendToBootClassPath()函數將dex cache和dex file關聯起來,同時把dex file註冊到boot_class_path_,dex cache註冊到dex_caches_。

1917void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, SirtRef& dex_cache) {
1918  CHECK(dex_cache.get() != NULL) << dex_file.GetLocation();
1919  boot_class_path_.push_back(&dex_file);
1920  RegisterDexFile(dex_file, dex_cache);
1921}

1938void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, SirtRef& dex_cache) {
1939  dex_lock_.AssertExclusiveHeld(Thread::Current());
1940  CHECK(dex_cache.get() != NULL) << dex_file.GetLocation();
1941  CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation()))
1942      << dex_cache->GetLocation()->ToModifiedUtf8() <<   << dex_file.GetLocation();
1943  dex_caches_.push_back(dex_cache.get());
1944  dex_cache->SetDexFile(&dex_file);
1945  dex_caches_dirty_ = true;
1946}

這些個註冊信息以後在ClassLinker調用FindClass()時會用到。Runtime的Create()和Init()函數完成後,在JNI_CreateJavaVM函數中Runtime的Start()函數被調用:

708bool Runtime::Start() {
709  VLOG(startup) << Runtime::Start entering;
710
711  CHECK(host_prefix_.empty()) << host_prefix_;
712
713  // Restore main thread state to kNative as expected by native code.
714  Thread* self = Thread::Current();
715  self->TransitionFromRunnableToSuspended(kNative);
716
717  started_ = true;
718
719  // InitNativeMethods needs to be after started_ so that the classes
720  // it touches will have methods linked to the oat file if necessary.
721  InitNativeMethods();
722
723  // Initialize well known thread group values that may be accessed threads while attaching.
724  InitThreadGroups(self);
725
726  Thread::FinishStartup();
727
728  if (is_zygote_) {
729    if (!InitZygote()) {
730      return false;
731    }
732  } else {
733    DidForkFromZygote();
734  }
735
736  StartDaemonThreads();
737
738  system_class_loader_ = CreateSystemClassLoader();
739
740  self->GetJniEnv()->locals.AssertEmpty();
741
742  VLOG(startup) << Runtime::Start exiting;
743
744  finished_starting_ = true;
745
746  return true;
747}

在InitNativeMethods()函數中,JniConstants::init()和WellKnownClasses::Init()通過FindClass(),GetStaticFieldID()和GetStaticMethodID()函數分別初始化瞭一大坨系統基本類,方法和域。這一撥都是最基本的類,沒初始化的話後面沒法玩瞭。然後RegisterRuntimeNativeMethods()函數註冊瞭一撥系統類中的Native函數:

1005void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
1006#define REGISTER(FN) extern void FN(JNIEnv*); FN(env)
1007  // Register Throwable first so that registration of other native methods can throw exceptions
1008  REGISTER(register_java_lang_Throwable);
…

接著InitNativeMethods()會load libjavacore.so這個庫,單獨load它是因為它本身包含瞭System.loadLibrary()實現,不先load它會導致雞與蛋的問題:

965    if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, reason)) {

回到Runtime::Start(),進行線程初始化,再判斷是否為Zygote進程,如果是的話要調用InitZygote()進行初始化(其中主要是mount一些文件系統),否則調用DidForkFromZygote()(其中會創建線程池,創建signalcatcher線程和啟動JDWP

調試線程)。再就是啟動後臺線程,然後用JNI調用java.lang.ClassLoader.getSystemClassLoader()得到系統的ClassLoader(由createSystemClassLoader()創建),一會調用com.android.internal.os.ZygoteInit.main()時用的就是它。

736  StartDaemonThreads();
737
738  system_class_loader_ = CreateSystemClassLoader();

從StartVM()返回後,AndroidRuntime執行startReg()來做瞭一件比較tricky的事情。它會在創建線程時加一個hook函數,這樣每個Thread起來時會先去執行AndroidRuntime::javaThreadShell(),而該函數會初始化Java虛擬機環境,這樣新建的線程就可以調用Java層瞭。

AndroidRuntime::startReg()
1228    /*
1229     * This hook causes all future threads created in this process to be
1230     * attached to the JavaVM.  (This needs to go away in favor of JNI
1231     * Attach calls.)
1232     */
1233    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

AndroidRuntime中的start()函數流程圖大體如下:

data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20140307/2014030715094722.jpg

 

基本的初始化工作完成後,接下來就可以執行主函數瞭。基本步驟是:

1. 先通過FindClass()找到相應的類。

2.再通過GetStaticMethodID()找到相應的方法。

3.最後就可以調用CallStaticVoidMethod()進入Java世界執行托管代碼瞭。

以Zygote初始化為例,其中類名為com.android.internal.os.ZygoteInit,方法為main。

871    /*
872     * Start VM.  This thread becomes the main thread of the VM, and will
873     * not return until the VM exits.
874     */
875    char* slashClassName = toSlashClassName(className);
876    jclass startClass = env->FindClass(slashClassName);
877    if (startClass == NULL) {
878        ALOGE(JavaVM unable to locate class '%s'
, slashClassName);
879        /* keep going */
880    } else {
881        jmethodID startMeth = env->GetStaticMethodID(startClass, main,
882            ([Ljava/lang/String;)V);
883        if (startMeth == NULL) {
884            ALOGE(JavaVM unable to find main() in '%s'
, className);
885            /* keep going */
886        } else {
887            env->CallStaticVoidMethod(startClass, startMeth, strArray);
888
889#if 0
890            if (env->ExceptionCheck())
891                threadExitUncaughtException(env);
892#endif
893        }
894    }

拋開雜七雜八的預處理,主要的就是三個函數:FindClass(),GetStaticMethodID()和CallStaticVoidMethod()。FindClass()的實現在/art/runtime/jni_internal.cc:

640  static jclass FindClass(JNIEnv* env, const char* name) {
641    CHECK_NON_NULL_ARGUMENT(FindClass, name);
642    Runtime* runtime = Runtime::Current();
643    ClassLinker* class_linker = runtime->GetClassLinker();
644    std::string descriptor(NormalizeJniClassDescriptor(name));
645    ScopedObjectAccess soa(env);
646    Class* c = NULL;
647    if (runtime->IsStarted()) {
648      ClassLoader* cl = GetClassLoader(soa);
649      c = class_linker->FindClass(descriptor.c_str(), cl);
650    } else {
651      c = class_linker->FindSystemClass(descriptor.c_str());
652    }
653    return soa.AddLocalReference(c);
654  }

在這裡GetClassLoader()調用GetSystemClassLoader()得到前面初始化好的系統ClassLoader。

267  ClassLoader* class_loader = soa.Decode(Runtime::Current()->GetSystemClassLoader());

接著調用ClassLinker的FindClass()查找目標類,其中涉及幾個關鍵函數:LookupClass(),DefineClass(),InsertClass(),LoadClass()和LinkClass()。以下簡要介紹下它們的主要功能:
LookupClass()先在ClassLinker的成員變量class_table_中找指定類,找到就返回,找不到的話看是否要在image中找(class_loader為NULL且dex_cache_image_class_lookup_required為true)。如果要的話就調用LookupClassFromImage()在Image中進行查找,找到瞭就調用InsertClass()將找到的類放入到class_table_中方便下次查找。
DefineClass()做瞭很多事情,包括瞭LoadClass(),InsertClass()和LinkClass()等動作。其中,LoadClass()調用LoadField()和LoadMethod()等函數把類中的域和方法數據從dex文件中讀出來,填入Class結構。
InsertClass()主要功能是把該類寫入class_table_中方便下次查找。
LinkClass()顧名思義,就是動態綁定虛函數和接口函數瞭。其調用結構:
LinkSuperClass() // 檢查父類。
LinkMethods()
LinkVirtualMethods() // 結合父類進行虛函數綁定,填寫Class中的虛函數表vtable_。
LinkInterfaceMethods() //處理接口類函數信息iftable_。註意接口類中的虛函數也會影響虛函數表,因此會更新vtable_。
LinkInstanceFields() & LinkStaticFields() // 更新域信息,如域中的Offset和類的對象大小等。

對於FindClass()來說,類大體有以下幾種情況:內置類,啟動類,系統類和其它。內置類是很基本的類,一般是初始化時預加載好的(如WellKnownClasses和JniConstants裡那一坨),它們可以通過LookupClassFromImage()函數找到。啟動類是在BOOTCLASSPATH裡的類,由於它們是啟動類,所以這裡還沒有ClassLoader。除掉前面的內置類,其餘的通過DexFile::FindInClassPath()查找得到。再就是系統類和其它類,它們的加載過程是類似的,都是通過ClassLoader的loadClass方法,區別在於前者通過特殊的SystemClassLoader進行加載。舉例來說,對於一個還沒被加載過的啟動類,一般流程是這樣的:

data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20140307/2014030715094724.jpg

主要涉及到的類有以下幾個,最核心的是Class類:

data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20140307/2014030715094725.jpg

回到FindClass()函數,由於在調用ZygoteInit.main()時,所需的類在初始化時都已經裝載鏈接好瞭。因此,這裡走的不是上面的流程,而是直接通過JNI調用ClassLoader.loadClass()進行裝載。完成後將找到的類轉為jclass返回給AndroidRuntime。類的查找結束後,就可以找相應的方法瞭。GetStaticMethodID會調用FindMethodID()函數,它首先對該類進行驗證,保證這個類是初始化好的,再調用其它函數進行目標函數的查找。關鍵幾行如下:

235  ArtMethod* method = NULL;
236  if (is_static) {
237    method = c->FindDirectMethod(name, sig);
238  } else {
239    method = c->FindVirtualMethod(name, sig);
240    if (method == NULL) {
241      // No virtual method matching the signature.  Search declared
242      // private methods and constructors.
243      method = c->FindDeclaredDirectMethod(name, sig);
244    }
245  }

可以看到,如果要找的是靜態函數(通過GetStaticMethodID()過來的),則調用FindDirectMethod()查找該類及其父類的非虛函數(通過Class的成員變量direct_methods_),否則調用FindVirtualMethod()查找該類及其父類的虛函數(通過Class的成員變量virtual_methods_),沒找到的話再調用FindDeclaredDirectMethod()查找該類的非虛函數。找到的條件是函數名和函數簽名相同,如這裡是main和([Ljava/lang/String;)V。找到目標函數後,就可以執行瞭。以下是執行目標函數的主幹部分:

246void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
247                       char result_type) {
…
253  // Push a transition back into managed code onto the linked list in thread.
254  ManagedStack fragment;
255  self->PushManagedStackFragment(&fragment);
…
270#ifdef ART_USE_PORTABLE_COMPILER
271      (*art_portable_invoke_stub)(this, args, args_size, self, result, result_type);
272#else
273      (*art_quick_invoke_stub)(this, args, args_size, self, result, result_type);
274#endif
…
297  // Pop transition.
298  self->PopManagedStackFragment(fragment);

前後分別是對托管代碼棧的保存和恢復。要執行托管代碼前,要先為其創建棧。這些棧通過ManagedStack的成員link_形成一個先入後出的鏈表。當執行完托管代碼後,隻要將最近放入的托管代碼棧恢復回來即可。中間是目標函數的執行,但在跳入目標函數體前還需要先執行一些ABI層的上下文處理代碼,這段代碼稱為stub。首先按ART_USE_PORTABLE_COMPILER來決定是用art_quick_invok_stub還是art_portable_invok_stub。由於它們是由匯編寫成,平臺相關,所以每個體系結構(x86, arm, mps)都有其實現。以x86體系為例,art_portable_invoke_stub定義在portable_entrypoints_x86.S中:

30DEFINE_FUNCTION art_portable_invoke_stub
31    PUSH ebp                      // save ebp
32    PUSH ebx                      // save ebx
33    mov %esp, %ebp                // copy value of stack pointer into base pointer
34    .cfi_def_cfa_register ebp
35    mov 20(%ebp), %ebx            // get arg array size
36    addl LITERAL(28), %ebx        // reserve space for return addr, method*, ebx, and ebp in frame
37    andl LITERAL(0xFFFFFFF0), %ebx    // align frame size to 16 bytes
38    subl LITERAL(12), %ebx        // remove space for return address, ebx, and ebp
39    subl %ebx, %esp               // reserve stack space for argument array
40    lea  4(%esp), %eax            // use stack pointer + method ptr as dest for memcpy
41    pushl 20(%ebp)                // push size of region to memcpy
42    pushl 16(%ebp)                // push arg array as source of memcpy
43    pushl %eax                    // push stack pointer as destination of memcpy
44    call SYMBOL(memcpy)           // (void*, const void*, size_t)
45    addl LITERAL(12), %esp        // pop arguments to memcpy
46    mov 12(%ebp), %eax            // move method pointer into eax
47    mov %eax, (%esp)              // push method pointer onto stack
48    call *METHOD_CODE_OFFSET(%eax) // call the method
49    mov %ebp, %esp                // restore stack pointer
50    POP ebx                       // pop ebx
51    POP ebp                       // pop ebp
52    mov 20(%esp), %ecx            // get result pointer
53    cmpl LITERAL(68), 24(%esp)    // test if result type char == 'D'
54    je return_double_portable
55    cmpl LITERAL(70), 24(%esp)    // test if result type char == 'F'
56    je return_float_portable
57    mov %eax, (%ecx)              // store the result
58    mov %edx, 4(%ecx)             // store the other half of the result
59    ret
60return_double_portable:
61    fstpl (%ecx)                  // store the floating point result as double
62    ret
63return_float_portable:
64    fstps (%ecx)                  // store the floating point result as float
65    ret
66END_FUNCTION art_portable_invoke_stub

可以看到,這是x86體系中的函數調用過程。首先保存棧幀等信息,然後把參數數組拷貝到棧中,再執行call指令跳轉到要執行的目標函數。METHOD_CODE_OFFSET指向ArtMethod中的成員變量entry_point_from_compiled_code_,也就是編譯好的目標函數的地址。接下來,就是等目標函數愉快地執行完,然後恢復上下文,保存返回值,最後執行ret指令返回。查找目標函數和執行的過程比較直觀:
data-cke-saved-src=https://www.aiwalls.com/uploadfile/Collfiles/20140307/2014030715094826.jpg

這樣,執行完托管代碼後,返回到AndroidRuntimie::start()函數,調用DetachCurrentThread()和DestroyJavaVM()來做清理工作,關閉虛擬機,收工。

897    ALOGD(Shutting down VM
);
898    if (mJavaVM->DetachCurrentThread() != JNI_OK)
899        ALOGW(Warning: unable to detach main thread
);
900    if (mJavaVM->DestroyJavaVM() != 0)
901        ALOGW(Warning: VM did not shut down cleanly
);

 

發佈留言