Android art模式解析

Posted Jarlene

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android art模式解析相关的知识,希望对你有一定的参考价值。

android art模式解析

本文主要针对android系统art模式下面从安装apk到运行apk的一个过程,主要有一下几个方面:

  • Art虚拟机介绍
  • 安装时dex文件转化为oat文件
  • oat文件对应的内存map(Elf)
  • Art加载类的过程

Art虚拟机介绍

Art是和Dalvik类似的虚拟机,所不同的是Dalvik虚拟机执行的是dex字节码,Art虚拟机执行的是本地机器码,这也是Google为了解决android性能问题所采取的方法,让Art虚拟机直接执行本地机器码,以提高性能。那么Art虚拟是怎么来的呢?

首先在Dalvik时代,dalvik虚拟机是从Zygote进程复制进来的,通过调用AndroidRuntime::start这个函数开始创建的(具体查看frameworks/base/core/jni/AndroidRuntime.cpp)。进入Art时代后,Zygote孵化器同样孵化出Art虚拟机。

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

    int result = -1;
    JavaVMInitArgs initArgs;
    JavaVMOption opt;
    char propBuf[PROPERTY_VALUE_MAX];
    char stackTraceFileBuf[PROPERTY_VALUE_MAX];
    char dexoptFlagsBuf[PROPERTY_VALUE_MAX];
    char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
    char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
    char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
    char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
    char heapgrowthlimitOptsBuf[sizeof("-XX:HeapGrowthLimit=")-1 + PROPERTY_VALUE_MAX];
    char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX];
    char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX];
    char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
    char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX];
    char extraOptsBuf[PROPERTY_VALUE_MAX];
    char* stackTraceFile = NULL;
    bool checkJni = false;
    bool checkDexSum = false;
    bool logStdio = false;
    enum 
      kEMDefault,
      kEMIntPortable,
      kEMIntFast,
      kEMJitCompiler,
     executionMode = kEMDefault;


    property_get("dalvik.vm.checkjni", propBuf, "");
    if (strcmp(propBuf, "true") == 0) 
        checkJni = true;
     else if (strcmp(propBuf, "false") != 0) 
        /* property is neither true nor false; fall back on kernel parameter */
        property_get("ro.kernel.android.checkjni", propBuf, "");
        if (propBuf[0] == '1') 
            checkJni = true;
        
    

    property_get("dalvik.vm.execution-mode", propBuf, "");
    if (strcmp(propBuf, "int:portable") == 0) 
        executionMode = kEMIntPortable;
     else if (strcmp(propBuf, "int:fast") == 0) 
        executionMode = kEMIntFast;
     else if (strcmp(propBuf, "int:jit") == 0) 
        executionMode = kEMJitCompiler;
    

    property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, "");

    property_get("dalvik.vm.check-dex-sum", propBuf, "");
    if (strcmp(propBuf, "true") == 0) 
        checkDexSum = true;
    

    property_get("log.redirect-stdio", propBuf, "");
    if (strcmp(propBuf, "true") == 0) 
        logStdio = true;
    

    strcpy(enableAssertBuf, "-ea:");
    property_get("dalvik.vm.enableassertions", enableAssertBuf+4, "");

    strcpy(jniOptsBuf, "-Xjniopts:");
    property_get("dalvik.vm.jniopts", jniOptsBuf+10, "");

    /* route exit() to our handler */
    opt.extraInfo = (void*) runtime_exit;
    opt.optionString = "exit";
    mOptions.add(opt);

    /* route fprintf() to our handler */
    opt.extraInfo = (void*) runtime_vfprintf;
    opt.optionString = "vfprintf";
    mOptions.add(opt);

    /* register the framework-specific "is sensitive thread" hook */
    opt.extraInfo = (void*) runtime_isSensitiveThread;
    opt.optionString = "sensitiveThread";
    mOptions.add(opt);

    opt.extraInfo = NULL;

    /* enable verbose; standard options are  jni, gc, class  */
    //options[curOpt++].optionString = "-verbose:jni";
    opt.optionString = "-verbose:gc";
    mOptions.add(opt);
    //options[curOpt++].optionString = "-verbose:class";

    /*
     * The default starting and maximum size of the heap.  Larger
     * values should be specified in a product property override.
     */
    strcpy(heapstartsizeOptsBuf, "-Xms");
    property_get("dalvik.vm.heapstartsize", heapstartsizeOptsBuf+4, "4m");
    opt.optionString = heapstartsizeOptsBuf;
    mOptions.add(opt);
    strcpy(heapsizeOptsBuf, "-Xmx");
    property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
    opt.optionString = heapsizeOptsBuf;
    mOptions.add(opt);

    // Increase the main thread's interpreter stack size for bug 6315322.
    opt.optionString = "-XX:mainThreadStackSize=24K";
    mOptions.add(opt);

    // Set the max jit code cache size.  Note: size of 0 will disable the JIT.
    strcpy(jitcodecachesizeOptsBuf, "-Xjitcodecachesize:");
    property_get("dalvik.vm.jit.codecachesize", jitcodecachesizeOptsBuf+19,  NULL);
    if (jitcodecachesizeOptsBuf[19] != '\\0') 
      opt.optionString = jitcodecachesizeOptsBuf;
      mOptions.add(opt);
    

    strcpy(heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
    property_get("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf+20, "");
    if (heapgrowthlimitOptsBuf[20] != '\\0') 
        opt.optionString = heapgrowthlimitOptsBuf;
        mOptions.add(opt);
    

    strcpy(heapminfreeOptsBuf, "-XX:HeapMinFree=");
    property_get("dalvik.vm.heapminfree", heapminfreeOptsBuf+16, "");
    if (heapminfreeOptsBuf[16] != '\\0') 
        opt.optionString = heapminfreeOptsBuf;
        mOptions.add(opt);
    

    strcpy(heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
    property_get("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf+16, "");
    if (heapmaxfreeOptsBuf[16] != '\\0') 
        opt.optionString = heapmaxfreeOptsBuf;
        mOptions.add(opt);
    

    strcpy(heaptargetutilizationOptsBuf, "-XX:HeapTargetUtilization=");
    property_get("dalvik.vm.heaptargetutilization", heaptargetutilizationOptsBuf+26, "");
    if (heaptargetutilizationOptsBuf[26] != '\\0') 
        opt.optionString = heaptargetutilizationOptsBuf;
        mOptions.add(opt);
    

    property_get("ro.config.low_ram", propBuf, "");
    if (strcmp(propBuf, "true") == 0) 
      opt.optionString = "-XX:LowMemoryMode";
      mOptions.add(opt);
    

    /*
     * Enable or disable dexopt features, such as bytecode verification and
     * calculation of register maps for precise GC.
     */
    property_get("dalvik.vm.dexopt-flags", dexoptFlagsBuf, "");
    if (dexoptFlagsBuf[0] != '\\0') 
        const char* opc;
        const char* val;

        opc = strstr(dexoptFlagsBuf, "v=");     /* verification */
        if (opc != NULL) 
            switch (*(opc+2)) 
            case 'n':   val = "-Xverify:none";      break;
            case 'r':   val = "-Xverify:remote";    break;
            case 'a':   val = "-Xverify:all";       break;
            default:    val = NULL;                 break;
            

            if (val != NULL) 
                opt.optionString = val;
                mOptions.add(opt);
            
        

        opc = strstr(dexoptFlagsBuf, "o=");     /* optimization */
        if (opc != NULL) 
            switch (*(opc+2)) 
            case 'n':   val = "-Xdexopt:none";      break;
            case 'v':   val = "-Xdexopt:verified";  break;
            case 'a':   val = "-Xdexopt:all";       break;
            case 'f':   val = "-Xdexopt:full";      break;
            default:    val = NULL;                 break;
            

            if (val != NULL) 
                opt.optionString = val;
                mOptions.add(opt);
            
        

        opc = strstr(dexoptFlagsBuf, "m=y");    /* register map */
        if (opc != NULL) 
            opt.optionString = "-Xgenregmap";
            mOptions.add(opt);

            /* turn on precise GC while we're at it */
            opt.optionString = "-Xgc:precise";
            mOptions.add(opt);
        
    

    /* enable debugging; set suspend=y to pause during VM init */
    /* use android ADB transport */
    opt.optionString =
        "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
    mOptions.add(opt);

    ALOGD("CheckJNI is %s\\n", checkJni ? "ON" : "OFF");
    if (checkJni) 
        /* extended JNI checking */
        opt.optionString = "-Xcheck:jni";
        mOptions.add(opt);

        /* set a cap on JNI global references */
        opt.optionString = "-Xjnigreflimit:2000";
        mOptions.add(opt);

        /* with -Xcheck:jni, this provides a JNI function call trace */
        //opt.optionString = "-verbose:jni";
        //mOptions.add(opt);
    

    char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
    property_get("dalvik.vm.lockprof.threshold", propBuf, "");
    if (strlen(propBuf) > 0) 
      strcpy(lockProfThresholdBuf, "-Xlockprofthreshold:");
      strcat(lockProfThresholdBuf, propBuf);
      opt.optionString = lockProfThresholdBuf;
      mOptions.add(opt);
    

    /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
    char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
    property_get("dalvik.vm.jit.op", propBuf, "");
    if (strlen(propBuf) > 0) 
        strcpy(jitOpBuf, "-Xjitop:");
        strcat(jitOpBuf, propBuf);
        opt.optionString = jitOpBuf;
        mOptions.add(opt);
    

    /* Force interpreter-only mode for selected methods */
    char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
    property_get("dalvik.vm.jit.method", propBuf, "");
    if (strlen(propBuf) > 0) 
        strcpy(jitMethodBuf, "-Xjitmethod:");
        strcat(jitMethodBuf, propBuf);
        opt.optionString = jitMethodBuf;
        mOptions.add(opt);
    

    if (executionMode == kEMIntPortable) 
        opt.optionString = "-Xint:portable";
        mOptions.add(opt);
     else if (executionMode == kEMIntFast) 
        opt.optionString = "-Xint:fast";
        mOptions.add(opt);
     else if (executionMode == kEMJitCompiler) 
        opt.optionString = "-Xint:jit";
        mOptions.add(opt);
    

    if (checkDexSum) 
        /* perform additional DEX checksum tests */
        opt.optionString = "-Xcheckdexsum";
        mOptions.add(opt);
    

    if (logStdio) 
        /* convert stdout/stderr to log messages */
        opt.optionString = "-Xlog-stdio";
        mOptions.add(opt);
    

    if (enableAssertBuf[4] != '\\0') 
        /* accept "all" to mean "all classes and packages" */
        if (strcmp(enableAssertBuf+4, "all") == 0)
            enableAssertBuf[3] = '\\0';
        ALOGI("Assertions enabled: '%s'\\n", enableAssertBuf);
        opt.optionString = enableAssertBuf;
        mOptions.add(opt);
     else 
        ALOGV("Assertions disabled\\n");
    

    if (jniOptsBuf[10] != '\\0') 
        ALOGI("JNI options: '%s'\\n", jniOptsBuf);
        opt.optionString = jniOptsBuf;
        mOptions.add(opt);
    

    if (stackTraceFileBuf[0] != '\\0') 
        static const char* stfOptName = "-Xstacktracefile:";

        stackTraceFile = (char*) malloc(strlen(stfOptName) +
            strlen(stackTraceFileBuf) +1);
        strcpy(stackTraceFile, stfOptName);
        strcat(stackTraceFile, stackTraceFileBuf);
        opt.optionString = stackTraceFile;
        mOptions.add(opt);
    

    /* extra options; parse this late so it overrides others */
    property_get("dalvik.vm.extra-opts", extraOptsBuf, "");
    parseExtraOpts(extraOptsBuf);

    /* Set the properties for locale */
    
        char langOption[sizeof("-Duser.language=") + 3];
        char regionOption[sizeof("-Duser.region=") + 3];
        strcpy(langOption, "-Duser.language=");
        strcpy(regionOption, "-Duser.region=");
        readLocale(langOption, regionOption);
        opt.extraInfo = NULL;
        opt.optionString = langOption;
        mOptions.add(opt);
        opt.optionString = regionOption;
        mOptions.add(opt);
    

    /*
     * We don't have /tmp on the device, but we often have an SD card.  Apps
     * shouldn't use this, but some test suites might want to exercise it.
     */
    opt.optionString = "-Djava.io.tmpdir=/sdcard";
    mOptions.add(opt);

    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;

    /*
     * 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;
    

    result = 0;

bail:
    free(stackTraceFile);
    return result;

这个方法主要是配置了一下虚拟机属性,到最后才去创建虚拟机实例。(代码:frameworks/base/core/jni/AndroidRuntime.cpp)

if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) 
        ALOGE("JNI_CreateJavaVM failed\\n");
        goto bail;

而JNI_CreateJavaVm方法执行如下:

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) 
  const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
  if (IsBadJniVersion(args->version)) 
    LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
    return JNI_EVERSION;
  
  RuntimeOptions options;
  for (int i = 0; i < args->nOptions; ++i) 
    JavaVMOption* option = &args->options[i];
    options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
  
  bool ignore_unrecognized = args->ignoreUnrecognized;
  if (!Runtime::Create(options, ignore_unrecognized)) 
    return JNI_ERR;
  
  Runtime* runtime = Runtime::Current();
  bool started = runtime->Start();
  if (!started) 
    delete Thread::Current()->GetJniEnv();
    delete runtime->GetJavaVM();
    LOG(WARNING) << "CreateJavaVM failed";
    return JNI_ERR;
  
  *p_env = Thread::Current()->GetJniEnv();
  *p_vm = runtime->GetJavaVM();
  return JNI_OK;

这个方法主要是根据传进来的参数配置一下环境,同时初始化JavaVM和JniEnv(代码:art/runtime/jni_internal.cc; art/runtime/runtime.cc)。到这里为止就将Art虚拟机创建出来了。

安装时dex文件转化为oat文件

我们知道,在dalvik虚拟机时代,在安装apk的时候,会将dex文件展开执行dexOpt,生成odex文件,这个就是对dex文件一次优化。而在Art虚拟机时代,在安装apk的时,会将dex文件转换成oat文件(oat文件是一种类似与Elf文件的存在),那么具体过程是怎么样的呢?

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));

这个是执行dex2oat的开始,从代码看出,做了一些生成oat的配置(包括路径,文件名等)。最终走到 execl方法执行生成oat文件(代码:frameworks/native/cmds/installd/commands.c)。至于dex是怎么生成oat,怎么翻译成机器码的,由于比较复杂且不影响后面所以介绍的东西,故不做介绍。

oat文件对应的内存map(Elf)

在Dalvik时代,dalvik加载dex文件也是有一定格式(内存),具体如下图:

struct DexFile 
    /* directly-mapped "opt" header */
    const DexOptHeader* pOptHeader;

    /* pointers to directly-mapped structs and arrays in base DEX */
    const DexHeader*    pHeader;
    const DexStringId*  pStringIds;
    const DexTypeId*    pTypeIds;
    const DexFieldId*   pFieldIds;
    const DexMethodId*  pMethodIds;
    const DexProtoId*   pProtoIds;
    const DexClassDef*  pClassDefs;
    const DexLink*      pLinkData;

    /*
     * These are mapped out of the "auxillary" section, and may not be
     * included in the file.
     */
    const DexClassLookup* pClassLookup;
    const void*         pRegisterMapPool;       // RegisterMapClassPool

    /* points to start of DEX file data */
    const u1*           baseAddr;

    /* track memory overhead for auxillary structures */
    int                 overhead;

    /* additional app-specific data structures associated with the DEX */
    //void*               auxData;
;

文件格式相对应的代码(代码:dalvik/vm/DvmDex.cpp)。

下面是Elf文件格式。

  // +-------------------------+
  // | Elf32_Ehdr              |
  // +-------------------------+
  // | Elf32_Phdr PHDR         |
  // | Elf32_Phdr LOAD R       | .dynsym .dynstr .hash .rodata
  // | Elf32_Phdr LOAD R X     | .text
  // | Elf32_Phdr LOAD RW      | .dynamic
  // | Elf32_Phdr DYNAMIC      | .dynamic
  // +-------------------------+
  // | .dynsym                 |
  // | Elf32_Sym  STN_UNDEF    |
  // | Elf32_Sym  oatdata      |
  // | Elf32_Sym  oatexec      |
  // | Elf32_Sym  oatlastword  |
  // +-------------------------+
  // | .dynstr                 |
  // | \\0                      |
  // | oatdata\\0               |
  // | oatexec\\0               |
  // | oatlastword\\0           |
  // | boot.oat\\0              |
  // +-------------------------+
  // | .hash                   |
  // | Elf32_Word nbucket = 1  |
  // | Elf32_Word nchain  = 3  |
  // | Elf32_Word bucket[0] = 0|
  // | Elf32_Word chain[0]  = 1|
  // | Elf32_Word chain[1]  = 2|
  // | Elf32_Word chain[2]  = 3|
  // +-------------------------+
  // | .rodata                 |
  // | oatdata..oatexec-4      |
  // +-------------------------+
  // | .text                   |
  // | oatexec..oatlastword    |
  // +-------------------------+
  // | .dynamic                |
  // | Elf32_Dyn DT_SONAME     |
  // | Elf32_Dyn DT_HASH       |
  // | Elf32_Dyn DT_SYMTAB     |
  // | Elf32_Dyn DT_SYMENT     |
  // | Elf32_Dyn DT_STRTAB     |
  // | Elf32_Dyn DT_STRSZ      |
  // | Elf32_Dyn DT_NULL       |
  // +-------------------------+
  // | .shstrtab               |
  // | \\0                      |
  // | .dynamic\\0              |
  // | .dynsym\\0               |
  // | .dynstr\\0               |
  // | .hash\\0                 |
  // | .rodata\\0               |
  // | .text\\0                 |
  // | .shstrtab\\0             |
  // +-------------------------+
  // | Elf32_Shdr NULL         |
  // | Elf32_Shdr .dynsym      |
  // | Elf32_Shdr .dynstr      |
  // | Elf32_Shdr .hash        |
  // | Elf32_Shdr .text        |
  // | Elf32_Shdr .rodata      |
  // | Elf32_Shdr .dynamic     |
  // | Elf32_Shdr .shstrtab    |
  // +-------------------------+
// OatHeader         variable length with count of D OatDexFiles
//
// OatDexFile[0]     one variable sized OatDexFile with offsets to Dex and OatClasses
// OatDexFile[1]
// ...
// OatDexFile[D]
//
// Dex[0]            one variable sized DexFile for each OatDexFile.
// Dex[1]            these are literal copies of the input .dex files.
// ...
// Dex[D]
//
// OatClass[0]       one variable sized OatClass for each of C DexFile::ClassDefs
// OatClass[1]       contains OatClass entries with class status, offsets to code, etc.
// ...
// OatClass[C]
//
// GcMap             one variable sized blob with GC map.
// GcMap             GC maps are deduplicated.
// ...
// GcMap
//
// VmapTable         one variable sized VmapTable blob (quick compiler only).
// VmapTable         VmapTables are deduplicated.
// ...
// VmapTable
//
// MappingTable      one variable sized blob with MappingTable (quick compiler only).
// MappingTable      MappingTables are deduplicated.
// ...
// MappingTable
//
// padding           if necessary so that the following code will be page aligned
//
// OatMethodHeader   fixed size header for a CompiledMethod including the size of the MethodCode.
// MethodCode        one variable sized blob with the code of a CompiledMethod.
// OatMethodHeader   (OatMethodHeader, MethodCode) pairs are deduplicated.
// MethodCode
// ...
// OatMethodHeader
// MethodCode
//

从上面看,oat文件有以下基本结构:
OatHeader
OatDexFile 多个
Dex 多个
OatClass 多个

对应于elf和oat文件
至此,Oat文件格式就完全清晰了。

Art加载类的过程

从前面来看,art虚拟机从创建、dex转化为oat、oat文件格式都和Dalvik虚拟机有着类似之处,那么art虚拟机加载类有是怎么一回事呢?

在Dalvik时代,通过查找DexFile中的class来自找到相关的类,具体流程可以参考这篇文章。这篇文章详细介绍了Dex和ClassObject关系以及类的加载过程。但在Art时代,存在着一个Runtime单例,用来描述ART运行时。通过调用Runtime类的静态成员函数Current可以获得上述Runtime单例。获得了这个单例之后,就可以调用它的成员函数GetClassLinker来获得一个ClassLinker对象。
ClassLinker根据参数descriptor来加载不同类,

static jclass FindClass(JNIEnv* env, const char* name) 
    CHECK_NON_NULL_ARGUMENT(name);
    Runtime* runtime = Runtime::Current();
    ClassLinker* class_linker = runtime->GetClassLinker();
    std::string descriptor(NormalizeJniClassDescriptor(name));
    ScopedObjectAccess soa(env);
    mirror::Class* c = nullptr;
    if (runtime->IsStarted()) 
      StackHandleScope<1> hs(soa.Self());
      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa)));
      c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
     else 
      c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
    
    return soa.AddLocalReference<jclass>(c);
  

从Runtime中获取ClassLinker来加载类,ClassLinker根据runtime是否初始了来选择是否是加载系统类。而如果初始化了就调用FindClass,具体代码如下:

 mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
                                      Handle<mirror::ClassLoader> class_loader) 
  DCHECK_NE(*descriptor, '\\0') << "descriptor is empty string";
  DCHECK(self != nullptr);
  self->AssertNoPendingException();
  if (descriptor[1] == '\\0') 
    // only the descriptors of primitive types should be 1 character long, also avoid class lookup
    // for primitive classes that aren't backed by dex files.
    return FindPrimitiveClass(descriptor[0]);
  
  // Find the class in the loaded classes table.
  mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
  if (klass != nullptr) 
    return EnsureResolved(self, descriptor, klass);
  
  // Class is not yet loaded.
  if (descriptor[0] == '[') 
    return CreateArrayClass(self, descriptor, class_loader);
   else if (class_loader.Get() == nullptr) 
    // The boot class loader, search the boot class path.
    ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
    if (pair.second != nullptr) 
      return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
     else 
      // The boot class loader is searched ahead of the application class loader, failures are
      // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
      // trigger the chaining with a proper stack trace.
      mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
      self->SetException(ThrowLocation(), pre_allocated);
      return nullptr;
    
   else if (Runtime::Current()->UseCompileTimeClassPath()) 
    // First try with the bootstrap class loader.
    if (class_loader.Get() != nullptr) 
      klass = LookupClass(descriptor, nullptr);
      if (klass != nullptr) 
        return EnsureResolved(self, descriptor, klass);
      
    
    // If the lookup failed search the boot class path. We don't perform a recursive call to avoid
    // a NoClassDefFoundError being allocated.
    ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
    if (pair.second != nullptr) 
      return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
    
    // Next try the compile time class path.
    const std::vector<const DexFile*>* class_path;
    
      ScopedObjectAccessUnchecked soa(self);
      ScopedLocalRef<jobject> jclass_loader(soa.Env(),
                                            soa.AddLocalReference<jobject>(class_loader.Get()));
      class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
    
    pair = FindInClassPath(descriptor, *class_path);
    if (pair.second != nullptr) 
      return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
    
   else 
    ScopedObjectAccessUnchecked soa(self);
    mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, class_loader);
    if (klass != nullptr) 
      return klass;
    
    ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                soa.AddLocalReference<jobject>(class_loader.Get()));
    std::string class_name_string(DescriptorToDot(descriptor));
    ScopedLocalRef<jobject> result(soa.Env(), nullptr);
    
      ScopedThreadStateChange tsc(self, kNative);
      ScopedLocalRef<jobject> class_name_object(soa.Env(),
                                                soa.Env()->NewStringUTF(class_name_string.c_str()));
      if (class_name_object.get() == nullptr) 
        DCHECK(self->IsExceptionPending());  // OOME.
        return nullptr;
      
      CHECK(class_loader_object.get() != nullptr);
      result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
                                               WellKnownClasses::java_lang_ClassLoader_loadClass,
                                               class_name_object.get()));
    
    if (self->IsExceptionPending()) 
      // If the ClassLoader threw, pass that exception up.
      return nullptr;
     else if (result.get() == nullptr) 
      // broken loader - throw NPE to be compatible with Dalvik
      ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
                                                      class_name_string.c_str()).c_str());
      return nullptr;
     else 
      // success, return mirror::Class*
      return soa.Decode<mirror::Class*>(result.get());
    
  

  ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
  return nullptr;

ClassLinker根据descriptor的不同,来加载不同的类。首先判断是不是基本类,是基本类就调用FindPrimitiveClass;如果不是则查询已经加载的类调用LookupClass。当Class_loader参数为空的是,ClassLinker会调用FindInClassPath去查找类,FindInClassPath干的事情就是从DexFile组合ClassPathEntry,具体:

// Search a collection of DexFiles for a descriptor
ClassPathEntry FindInClassPath(const char* descriptor,
                               const std::vector<const DexFile*>& class_path) 
  for (size_t i = 0; i != class_path.size(); ++i) 
    const DexFile* dex_file = class_path[i];
    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
    if (dex_class_def != nullptr) 
      return ClassPathEntry(dex_file, dex_class_def);
    
  
  // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
  return ClassPathEntry(static_cast<const DexFile*>(nullptr),
                        static_cast<const DexFile::ClassDef*>(nullptr));

当生产ClassPathEntry ,会调用DefineClass,DefineClass主要是在DexFile中查找类,以及将查找到类放进一个表中,提供给LookupClass查询,而具体执行查找类是交给LoadClass来做:

mirror::Class* ClassLinker::DefineClass(const char* descriptor,
                                        Handle<mirror::ClassLoader> class_loader,
                                        const DexFile& dex_file,
                                        const DexFile::ClassDef& dex_class_def) 
  Thread* self = Thread::Current();
  StackHandleScope<3> hs(self);
  auto klass = hs.NewHandle<mirror::Class>(nullptr);
  bool should_allocate = false;

  // Load the class from the dex file.
  if (UNLIKELY(!init_done_)) 
    // finish up init of hand crafted class_roots_
    if (strcmp(descriptor, "Ljava/lang/Object;") == 0) 
      klass.Assign(GetClassRoot(kJavaLangObject));
     else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) 
      klass.Assign(GetClassRoot(kJavaLangClass));
     else if (strcmp(descriptor, "Ljava/lang/String;") == 0) 
      klass.Assign(GetClassRoot(kJavaLangString));
     else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) 
      klass.Assign(GetClassRoot(kJavaLangRefReference));
     else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) 
      klass.Assign(GetClassRoot(kJavaLangDexCache));
     else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) 
      klass.Assign(GetClassRoot(kJavaLangReflectArtField));
     else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) 
      klass.Assign(GetClassRoot(kJavaLangReflectArtMethod));
     else 
      should_allocate = true;
    
   else 
    should_allocate = true;
  

  if (should_allocate) 
    // Allocate a class with the status of not ready.
    // Interface object should get the right size here. Regular class will
    // figure out the right size later and be replaced with one of the right
    // size when the class becomes resolved.
    klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
  
  if (UNLIKELY(klass.Get() == nullptr)) 
    CHECK(self->IsExceptionPending());  // Expect an OOME.
    return nullptr;
  
  klass->SetDexCache(FindDexCache(dex_file));
  LoadClass(dex_file, dex_class_def, klass, class_loader.Get());
  ObjectLock<mirror::Class> lock(self, klass);
  if (self->IsExceptionPending()) 
    // An exception occured during load, set status to erroneous while holding klass' lock in case
    // notification is necessary.
    if (!klass->IsErroneous()) 
      klass->SetStatus(mirror::Class::kStatusError, self);
    
    return nullptr;
  
  klass->SetClinitThreadId(self->GetTid());

  // Add the newly loaded class to the loaded classes table.
  mirror::Class* existing = InsertClass(descriptor, klass.Get(), Hash(descriptor));
  if (existing != nullptr) 
    // We failed to insert because we raced with another thread. Calling EnsureResolved may cause
    // this thread to block.
    return EnsureResolved(self, descriptor, existing);
  

  // Finish loading (if necessary) by finding parents
  CHECK(!klass->IsLoaded());
  if (!LoadSuperAndInterfaces(klass, dex_file)) 
    // Loading failed.
    if (!klass->IsErroneous()) 
      klass->SetStatus(mirror::Class::kStatusError, self);
    
    return nullptr;
  
  CHECK(klass->IsLoaded());
  // Link the class (if necessary)
  CHECK(!klass->IsResolved());
  // TODO: Use fast jobjects?
  auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);

  mirror::Class* new_class = nullptr;
  if (!<

以上是关于Android art模式解析的主要内容,如果未能解决你的问题,请参考以下文章

Android类加载(一)——DVM、ART、Dexopt、DexAot名词解析

Android art模式 androidruntime

Android逆向进阶—— 脱壳的奥义(基ART模式下的dump)

安卓执行机制JNIDalvikART之间的比較 。android L 改动执行机制。

解析dex2oat的实现

基于Frida框架打造Art模式下的脱壳工具(OpenMemory)的原理分析