亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Android ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程

系統(tǒng) 4117 0

Android 4.4發(fā)布了一個(gè)ART運(yùn)行時(shí),準(zhǔn)備用來(lái)替換掉之前一直使用的Dalvik虛擬機(jī),希望籍此解決飽受詬病的性能問(wèn)題。老羅不打算分析ART的實(shí)現(xiàn)原理,只是很有興趣知道ART是如何無(wú)縫替換掉原來(lái)的Dalvik虛擬機(jī)的。畢竟在原來(lái)的系統(tǒng)中,大量的代碼都是運(yùn)行在Dalvik虛擬機(jī)里面的。開始覺(jué)得這個(gè)替換工作是挺復(fù)雜的,但是分析了相關(guān)代碼之后,發(fā)現(xiàn)思路是很清晰的。本文就詳細(xì)分析這個(gè)無(wú)縫的替換過(guò)程。

老羅的新浪微博: http://weibo.com/shengyangluo ,歡迎關(guān)注!

我們知道,Dalvik虛擬機(jī)實(shí)則也算是一個(gè)Java虛擬機(jī),只不過(guò)它執(zhí)行的不是class文件,而是dex文件。因此,ART運(yùn)行時(shí)最理想的方式也是實(shí)現(xiàn)為一個(gè)Java虛擬機(jī)的形式,這樣就可以很容易地將Dalvik虛擬機(jī)替換掉。注意,我們這里說(shuō)實(shí)現(xiàn)為Java虛擬機(jī)的形式,實(shí)際上是指提供一套完全與Java虛擬機(jī)兼容的接口。例如,Dalvik虛擬機(jī)在接口上與Java虛擬機(jī)是一致的,但是它的內(nèi)部可以是完全不一樣的東西。

實(shí)際上,ART運(yùn)行時(shí)就是真的和Dalvik虛擬機(jī)一樣,實(shí)現(xiàn)了一套完全兼容Java虛擬機(jī)的接口。為了方便描述,接下來(lái)我們就將ART運(yùn)行時(shí)稱為ART虛擬機(jī),它和Dalvik虛擬機(jī)、Java虛擬機(jī)的關(guān)系如圖1所示:

Android ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程分析

圖1 Java虛擬機(jī)、Dalvik虛擬機(jī)和ART運(yùn)行時(shí)的關(guān)系

從圖1可以知道,Dalvik虛擬機(jī)和ART虛擬機(jī)都實(shí)現(xiàn)了三個(gè)用來(lái)抽象Java虛擬機(jī)的接口:

1. JNI_GetDefaultJavaVMInitArgs -- 獲取虛擬機(jī)的默認(rèn)初始化參數(shù)

2. JNI_CreateJavaVM -- 在進(jìn)程中創(chuàng)建虛擬機(jī)實(shí)例

3. JNI_GetCreatedJavaVMs -- 獲取進(jìn)程中創(chuàng)建的虛擬機(jī)實(shí)例

在Android系統(tǒng)中,Davik虛擬機(jī)實(shí)現(xiàn)在libdvm.so中,ART虛擬機(jī)實(shí)現(xiàn)在libart.so中。也就是說(shuō),libdvm.so和libart.so導(dǎo)出了JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM和JNI_GetCreatedJavaVMs這三個(gè)接口,供外界調(diào)用。

此外,Android系統(tǒng)還提供了一個(gè)系統(tǒng)屬性persist.sys.dalvik.vm.lib,它的值要么等于libdvm.so,要么等于libart.so。當(dāng)?shù)扔趌ibdvm.so時(shí),就表示當(dāng)前用的是Dalvik虛擬機(jī),而當(dāng)?shù)扔趌ibart.so時(shí),就表示當(dāng)前用的是ART虛擬機(jī)。

以上描述的Dalvik虛擬機(jī)和ART虛擬機(jī)的共同之處,當(dāng)然它們之間最顯著還是不同之處。不同的地方就在于,Dalvik虛擬機(jī)執(zhí)行的是dex字節(jié)碼,ART虛擬機(jī)執(zhí)行的是本地機(jī)器碼。這意味著Dalvik虛擬機(jī)包含有一個(gè)解釋器,用來(lái)執(zhí)行dex字節(jié)碼,具體可以參考 Dalvik虛擬機(jī)簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃 這個(gè)系列的文章。當(dāng)然,Android從2.2開始,也包含有JIT(Just-In-Time),用來(lái)在運(yùn)行時(shí)動(dòng)態(tài)地將執(zhí)行頻率很高的dex字節(jié)碼翻譯成本地機(jī)器碼,然后再執(zhí)行。通過(guò)JIT,就可以有效地提高Dalvik虛擬機(jī)的執(zhí)行效率。但是,將dex字節(jié)碼翻譯成本地機(jī)器碼是發(fā)生在應(yīng)用程序的運(yùn)行過(guò)程中的,并且應(yīng)用程序每一次重新運(yùn)行的時(shí)候,都要做重做這個(gè)翻譯工作的。因此,即使用采用了JIT,Dalvik虛擬機(jī)的總體性能還是不能與直接執(zhí)行本地機(jī)器碼的ART虛擬機(jī)相比。

那么,ART虛擬機(jī)執(zhí)行的本地機(jī)器碼是從哪里來(lái)的呢?Android的運(yùn)行時(shí)從Dalvik虛擬機(jī)替換成ART虛擬機(jī),并不要求開發(fā)者要將重新將自己的應(yīng)用直接編譯成目標(biāo)機(jī)器碼。也就是說(shuō),開發(fā)者開發(fā)出的應(yīng)用程序經(jīng)過(guò)編譯和打包之后,仍然是一個(gè)包含dex字節(jié)碼的APK文件。既然應(yīng)用程序包含的仍然是dex字節(jié)碼,而ART虛擬機(jī)需要的是本地機(jī)器碼,這就必然要有一個(gè)翻譯的過(guò)程。這個(gè)翻譯的過(guò)程當(dāng)然不能發(fā)生應(yīng)用程序運(yùn)行的時(shí)候,否則的話就和Dalvik虛擬機(jī)的JIT一樣了。在計(jì)算機(jī)的世界里,與JIT相對(duì)的是AOT。AOT進(jìn)Ahead-Of-Time的簡(jiǎn)稱,它發(fā)生在程序運(yùn)行之前。我們用靜態(tài)語(yǔ)言(例如C/C++)來(lái)開發(fā)應(yīng)用程序的時(shí)候,編譯器直接就把它們翻譯成目標(biāo)機(jī)器碼。這種靜態(tài)語(yǔ)言的編譯方式也是AOT的一種。但是前面我們提到,ART虛擬機(jī)并不要求開發(fā)者將自己的應(yīng)用直接編譯成目標(biāo)機(jī)器碼。這樣,將應(yīng)用的dex字節(jié)碼翻譯成本地機(jī)器碼的最恰當(dāng)AOT時(shí)機(jī)就發(fā)生在應(yīng)用安裝的時(shí)候。

我們知道,沒(méi)有ART虛擬機(jī)之前,應(yīng)用在安裝的過(guò)程,其實(shí)也會(huì)執(zhí)行一次“翻譯”的過(guò)程。只不過(guò)這個(gè)“翻譯”的過(guò)程是將dex字節(jié)碼進(jìn)行優(yōu)化,也就是由dex文件生成odex文件。這個(gè)過(guò)程由安裝服務(wù)PackageManagerService請(qǐng)求守護(hù)進(jìn)程installd來(lái)執(zhí)行的。從這個(gè)角度來(lái)說(shuō),在應(yīng)用安裝的過(guò)程中將dex字節(jié)碼翻譯成本地機(jī)器碼對(duì)原來(lái)的應(yīng)用安裝流程基本上就不會(huì)產(chǎn)生什么影響。

有了以上的背景知識(shí)之后,我們接下來(lái)就從兩個(gè)角度來(lái)了解ART虛擬機(jī)是如何做到無(wú)縫替換Dalvik虛擬機(jī)的:

1. ART虛擬機(jī)的啟動(dòng)過(guò)程;

2. Dex字節(jié)碼翻譯成本地機(jī)器碼的過(guò)程。

我們知道,Android系統(tǒng)在啟動(dòng)的時(shí)候,會(huì)創(chuàng)建一個(gè)Zygote進(jìn)程,充當(dāng)應(yīng)用程序進(jìn)程孵化器。Zygote進(jìn)程在啟動(dòng)的過(guò)程中,又會(huì)創(chuàng)建一個(gè)Dalvik虛擬機(jī)。Zygote進(jìn)程是通過(guò)復(fù)制自己來(lái)創(chuàng)建新的應(yīng)用程序進(jìn)程的。這意味著Zygote進(jìn)程會(huì)將自己的Dalvik虛擬機(jī)復(fù)制給應(yīng)用程序進(jìn)程。通過(guò)這種方式就可以大大地提高應(yīng)用程序的啟動(dòng)速度,因?yàn)檫@種方式避免了每一個(gè)應(yīng)用程序進(jìn)程在啟動(dòng)的時(shí)候都要去創(chuàng)建一個(gè)Dalvik。事實(shí)上,Zygote進(jìn)程通過(guò)自我復(fù)制的方式來(lái)創(chuàng)建應(yīng)用程序進(jìn)程,省去的不僅僅是應(yīng)用程序進(jìn)程創(chuàng)建Dalvik虛擬機(jī)的時(shí)間,還能省去應(yīng)用程序進(jìn)程加載各種系統(tǒng)庫(kù)和系統(tǒng)資源的時(shí)間,因?yàn)樗鼈冊(cè)赯ygote進(jìn)程中已經(jīng)加載過(guò)了,并且也會(huì)連同Dalvik虛擬機(jī)一起復(fù)制到應(yīng)用程序進(jìn)程中去。關(guān)于Zygote進(jìn)程和應(yīng)用程序進(jìn)程啟動(dòng)的更多知識(shí),可以參考 Android系統(tǒng)進(jìn)程Zygote啟動(dòng)過(guò)程的源代碼分析 Android應(yīng)用程序進(jìn)程啟動(dòng)過(guò)程的源代碼分析 這兩篇文章。

即然應(yīng)用程序進(jìn)程里面的Dalvik虛擬機(jī)都是從Zygote進(jìn)程中復(fù)制過(guò)來(lái)的,那么接下來(lái)我們就繼續(xù)Zygote進(jìn)程是如何創(chuàng)建Dalvik虛擬機(jī)的。從 Dalvik虛擬機(jī)的啟動(dòng)過(guò)程分析 這篇文章可以知道,Zygote進(jìn)程中的Dalvik虛擬機(jī)是從AndroidRutime::start這個(gè)函數(shù)開始創(chuàng)建的。因此,接下來(lái)我們就看看這個(gè)函數(shù)的實(shí)現(xiàn):

    void AndroidRuntime::start(const char* className, const char* options)
{
    ......

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }

    ......

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    
    ......
}
  
這個(gè)函數(shù)定義在文件frameworks/base/core/jni/AndroidRuntime.cpp中。

AndroidRutime類的成員函數(shù)start最主要是做了以下三件事情:

1. 創(chuàng)建一個(gè)JniInvocation實(shí)例,并且調(diào)用它的成員函數(shù)init來(lái)初始化JNI環(huán)境;

2. 調(diào)用AndroidRutime類的成員函數(shù)startVm來(lái)創(chuàng)建一個(gè)虛擬機(jī)及其對(duì)應(yīng)的JNI接口,即創(chuàng)建一個(gè)JavaVM接口和一個(gè)JNIEnv接口;

3. 有了上述的JavaVM接口和JNIEnv接口之后,就可以在Zygote進(jìn)程中加載指定的class了。

其中,第1件事情和第2件事情又是最關(guān)鍵的。因此,接下來(lái)我們繼續(xù)分析它們所對(duì)應(yīng)的函數(shù)的實(shí)現(xiàn)。

JniInvocation類的成員函數(shù)init的實(shí)現(xiàn)如下所示:

    #ifdef HAVE_ANDROID_OS
static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib";
#endif
static const char* kLibraryFallback = "libdvm.so";

bool JniInvocation::Init(const char* library) {
#ifdef HAVE_ANDROID_OS
  char default_library[PROPERTY_VALUE_MAX];
  property_get(kLibrarySystemProperty, default_library, kLibraryFallback);
#else
  const char* default_library = kLibraryFallback;
#endif
  if (library == NULL) {
    library = default_library;
  }

  handle_ = dlopen(library, RTLD_NOW);
  if (handle_ == NULL) {
    if (strcmp(library, kLibraryFallback) == 0) {
      // Nothing else to try.
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
    // Note that this is enough to get something like the zygote
    // running, we can't property_set here to fix this for the future
    // because we are root and not the system user. See
    // RuntimeInit.commonInit for where we fix up the property to
    // avoid future fallbacks. http://b/11463182
    ALOGW("Falling back from %s to %s after dlopen error: %s",
          library, kLibraryFallback, dlerror());
    library = kLibraryFallback;
    handle_ = dlopen(library, RTLD_NOW);
    if (handle_ == NULL) {
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
                  "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
                  "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;
}
  
這個(gè)函數(shù)定義在文件libnativehelper/JniInvocation.cpp中。

JniInvocation類的成員函數(shù)init所做的事情很簡(jiǎn)單。它首先是讀取系統(tǒng)屬性persist.sys.dalvik.vm.lib的值。前面提到,系統(tǒng)屬性persist.sys.dalvik.vm.lib的值要么等于libdvm.so,要么等于libart.so。因此,接下來(lái)通過(guò)函數(shù)dlopen加載到進(jìn)程來(lái)的要么是libdvm.so,要么是libart.so。無(wú)論加載的是哪一個(gè)so,都要求它導(dǎo)出JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM和JNI_GetCreatedJavaVMs這三個(gè)接口,并且分別保存在JniInvocation類的三個(gè)成員變量JNI_GetDefaultJavaVMInitArgs_、JNI_CreateJavaVM_和JNI_GetCreatedJavaVMs_中。這三個(gè)接口也就是前面我們提到的用來(lái)抽象Java虛擬機(jī)的三個(gè)接口。

從這里就可以看出,JniInvocation類的成員函數(shù)init實(shí)際上就是根據(jù)系統(tǒng)屬性persist.sys.dalvik.vm.lib來(lái)初始化Dalvik虛擬機(jī)或者ART虛擬機(jī)環(huán)境。

接下來(lái)我們繼續(xù)看AndroidRutime類的成員函數(shù)startVm的實(shí)現(xiàn):

    int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
    ......

    /*
     * Initialize the VM.
     *
     * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
     * If this call succeeds, the VM is ready, and we can start issuing
     * JNI calls.
     */
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        goto bail;
    }

    ......
}
  
這個(gè)函數(shù)定義在文件frameworks/base/core/jni/AndroidRuntime.cpp中。

AndroidRutime類的成員函數(shù)startVm最主要就是調(diào)用函數(shù)JNI_CreateJavaVM來(lái)創(chuàng)建一個(gè)JavaVM接口及其對(duì)應(yīng)的JNIEnv接口:

    extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
}
  
這個(gè)函數(shù)定義在文件libnativehelper/JniInvocation.cpp中。

JniInvocation類的靜態(tài)成員函數(shù)GetJniInvocation返回的便是前面所創(chuàng)建的JniInvocation實(shí)例。有了這個(gè)JniInvocation實(shí)例之后,就繼續(xù)調(diào)用它的成員函數(shù)JNI_CreateJavaVM來(lái)創(chuàng)建一個(gè)JavaVM接口及其對(duì)應(yīng)的JNIEnv接口:

    jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
}
  
這個(gè)函數(shù)定義在文件libnativehelper/JniInvocation.cpp中。

JniInvocation類的成員變量JNI_CreateJavaVM_指向的就是前面所加載的libdvm.so或者libart.so所導(dǎo)出的函數(shù)JNI_CreateJavaVM,因此,JniInvocation類的成員函數(shù)JNI_CreateJavaVM返回的JavaVM接口指向的要么是Dalvik虛擬機(jī),要么是ART虛擬機(jī)。

通過(guò)上面的分析,我們就很容易知道,Android系統(tǒng)通過(guò)將ART運(yùn)行時(shí)抽象成一個(gè)Java虛擬機(jī),以及通過(guò)系統(tǒng)屬性persist.sys.dalvik.vm.lib和一個(gè)適配層JniInvocation,就可以無(wú)縫地將Dalvik虛擬機(jī)替換為ART運(yùn)行時(shí)。這個(gè)替換過(guò)程設(shè)計(jì)非常巧妙,因?yàn)樯婕暗降拇a修改是非常少的。

以上就是ART虛擬機(jī)的啟動(dòng)過(guò)程,接下來(lái)我們?cè)俜治鰬?yīng)用程序在安裝過(guò)程中將dex字節(jié)碼翻譯為本地機(jī)器碼的過(guò)程。

Android應(yīng)用程序的安裝過(guò)程可以參考 Android應(yīng)用程序安裝過(guò)程源代碼分析 這篇文章。簡(jiǎn)單來(lái)說(shuō),就是Android系統(tǒng)通過(guò)PackageManagerService來(lái)安裝APK,在安裝的過(guò)程,PackageManagerService會(huì)通過(guò)另外一個(gè)類Instalerl的成員函數(shù)dexopt來(lái)對(duì)APK里面的dex字節(jié)碼進(jìn)行優(yōu)化:

    public final class Installer {
    ......

    public int dexopt(String apkPath, int uid, boolean isPublic) {
        StringBuilder builder = new StringBuilder("dexopt");
        builder.append(' ');
        builder.append(apkPath);
        builder.append(' ');
        builder.append(uid);
        builder.append(isPublic ? " 1" : " 0");
        return execute(builder.toString());
    }

    ......
}
  
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/pm/Installer.java中。

Installer通過(guò)socket向守護(hù)進(jìn)程installd發(fā)送一個(gè)dexopt請(qǐng)求,這個(gè)請(qǐng)求是由installd里面的函數(shù)dexopt來(lái)處理的:

    int dexopt(const char *apk_path, uid_t uid, int is_public)
{
    struct utimbuf ut;
    struct stat apk_stat, dex_stat;
    char out_path[PKG_PATH_MAX];
    char dexopt_flags[PROPERTY_VALUE_MAX];
    char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX];
    char *end;
    int res, zip_fd=-1, out_fd=-1;

    ......

    /* The command to run depend ones the value of persist.sys.dalvik.vm.lib */
    property_get("persist.sys.dalvik.vm.lib", persist_sys_dalvik_vm_lib, "libdvm.so");

    /* Before anything else: is there a .odex file?  If so, we have
     * precompiled the apk and there is nothing to do here.
     */
    sprintf(out_path, "%s%s", apk_path, ".odex");
    if (stat(out_path, &dex_stat) == 0) {
        return 0;
    }

    if (create_cache_path(out_path, apk_path)) {
        return -1;
    }

    ......

    out_fd = open(out_path, O_RDWR | O_CREAT | O_EXCL, 0644);

    ......

    pid_t pid;
    pid = fork();
    if (pid == 0) {
        ......

        if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {
            run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
        } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {
            run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
        } else {
            exit(69);   /* Unexpected persist.sys.dalvik.vm.lib value */
        }
        exit(68);   /* only get here on exec failure */
    } 

    ......
}
  

這個(gè)函數(shù)定義在文件frameworks/native/cmds/installd/commands.c中。

函數(shù)dexopt首先是讀取系統(tǒng)屬性persist.sys.dalvik.vm.lib的值,接著在/data/dalvik-cache目錄中創(chuàng)建一個(gè)odex文件。這個(gè)odex文件就是作為dex文件優(yōu)化后的輸出文件。再接下來(lái),函數(shù)dexopt通過(guò)fork來(lái)創(chuàng)建一個(gè)子進(jìn)程。如果系統(tǒng)屬性persist.sys.dalvik.vm.lib的值等于libdvm.so,那么該子進(jìn)程就會(huì)調(diào)用函數(shù)run_dexopt來(lái)將dex文件優(yōu)化成odex文件。另一方面,如果系統(tǒng)屬性persist.sys.dalvik.vm.lib的值等于libart.so,那么該子進(jìn)程就會(huì)調(diào)用函數(shù)run_dex2oat來(lái)將dex文件優(yōu)化成oart文件,實(shí)際上就是將dex字節(jié)碼翻譯成本地機(jī)器碼,并且保存在一個(gè)oat文件中。

函數(shù)run_dexopt和run_dex2oat的實(shí)現(xiàn)如下所示:

    static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name,
    const char* output_file_name, const char* dexopt_flags)
{
    static const char* DEX_OPT_BIN = "/system/bin/dexopt";
    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
    char zip_num[MAX_INT_LEN];
    char odex_num[MAX_INT_LEN];

    sprintf(zip_num, "%d", zip_fd);
    sprintf(odex_num, "%d", odex_fd);

    ALOGV("Running %s in=%s out=%s\n", DEX_OPT_BIN, input_file_name, output_file_name);
    execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name,
        dexopt_flags, (char*) NULL);
    ALOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno));
}

static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
    const char* output_file_name, const char* dexopt_flags)
{
    static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
    char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];
    char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
    char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];
    char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX];

    sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
    sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
    sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
    sprintf(oat_location_arg, "--oat-location=%s", output_file_name);

    ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
    execl(DEX2OAT_BIN, DEX2OAT_BIN,
          zip_fd_arg, zip_location_arg,
          oat_fd_arg, oat_location_arg,
          (char*) NULL);
    ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
}
  
這兩個(gè)函數(shù)定義在文件frameworks/native/cmds/installd/commands.c中。

這從里就可以看出,函數(shù)run_dexopt通過(guò)調(diào)用/system/bin/dexopt來(lái)對(duì)dex字節(jié)碼進(jìn)行優(yōu)化,而函數(shù)run_dex2oat通過(guò)調(diào)用/system/bin/dex2oat來(lái)將dex字節(jié)碼翻譯成本地機(jī)器碼。注意,無(wú)論是對(duì)dex字節(jié)碼進(jìn)行優(yōu)化,還是將dex字節(jié)碼翻譯成本地機(jī)器碼,最終得到的結(jié)果都是保存在相同名稱的一個(gè)odex文件里面的,但是前者對(duì)應(yīng)的是一個(gè)dexy文件(表示這是一個(gè)優(yōu)化過(guò)的dex),后者對(duì)應(yīng)的是一個(gè)oat文件(實(shí)際上是一個(gè)自定義的elf文件,里面包含的都是本地機(jī)器指令)。通過(guò)這種方式,原來(lái)任何通過(guò)絕對(duì)路徑引用了該odex文件的代碼就都不需要修改了。

通過(guò)上面的分析,我們就很容易知道,只需要將dex文件的優(yōu)化過(guò)程替換成dex文件翻譯成本地機(jī)器碼的過(guò)程,就可以輕松地在應(yīng)用安裝過(guò)程,無(wú)縫地將Dalvik虛擬機(jī)替換成ART運(yùn)行時(shí)。

最后,還有一個(gè)地方需要注意的是,應(yīng)用程序的安裝發(fā)生在兩個(gè)時(shí)機(jī),第一個(gè)時(shí)機(jī)是系統(tǒng)啟動(dòng)的時(shí)候,第二個(gè)時(shí)機(jī)系統(tǒng)啟動(dòng)完成后用戶自行安裝的時(shí)候。在第一個(gè)時(shí)機(jī)中,系統(tǒng)除了會(huì)對(duì)/system/app和/data/app目錄下的所有APK進(jìn)行dex字節(jié)碼到本地機(jī)器碼的翻譯之外,還會(huì)對(duì)/system/framework目錄下的APK或者JAR文件,以及這些APK所引用的外部JAR,進(jìn)行dex字節(jié)碼到本地機(jī)器碼的翻譯。這樣就可以保證除了應(yīng)用之外,系統(tǒng)中使用Java來(lái)開發(fā)的系統(tǒng)服務(wù),也會(huì)統(tǒng)一地從dex字節(jié)碼翻譯成本地機(jī)器碼。也就是說(shuō),將Android系統(tǒng)中的Dalvik虛擬機(jī)替換成ART運(yùn)行時(shí)之后,系統(tǒng)中的代碼都是由ART運(yùn)行時(shí)來(lái)執(zhí)行的了,這時(shí)候就不會(huì)對(duì)Dalvik虛擬機(jī)產(chǎn)生任何的依賴。

至此,我們就分析完成ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程了,更多的干貨分享衣關(guān)注老羅的新浪微博: http://weibo.com/shengyangluo

Android ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程分析


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 日本高清不卡免费 | 成人欧美一区二区三区 | 久久精品国产亚洲香蕉 | 四虎在线影视 | 色yeye成人免费视频 | 欧美高清一区 | 天天摸天天干天天操 | 一区二区三区在线免费看 | 2022国内精品免费福利视频 | 国产精品嘿咻嘿咻在线播放 | 亚洲国产精品热久久 | 欧美性影院 | 国产亚洲精品一品区99热 | 日日操影院 | 国产一区二区高清在线 | 免费污视频在线观看 | 欧美 日韩 国产 在线 | 久久女人天堂 | 在线视频 中文字幕 | 色网址在线观看 | avtt男人天堂 | 欧美成人四级剧情在线播放 | 久久国产麻豆 | 久青草国产视频 | 中文字幕亚洲专区 | 91热成人精品国产免费 | 亚洲99久久久久综合 | 日韩欧美在线一级一中文字暮 | 亚洲成人国产精品 | 国产在线视频h | 久久久国产成人精品 | 99久久这里只精品麻豆 | 波多野结衣中文字幕久久 | 午夜精品久久久久久毛片 | 黄片毛片一级 | 久久国内 | 国产又黄又a又潮娇喘视频 国产又色又爽又黄又刺激18 | 六月丁香婷婷激情国产 | 欧美 日产 国产精选 | 欧美成人伦理 | 四虎成人影院网址 |