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+8digchar 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 DalvikThrowNullPointerException(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 resolvedreturn 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 parentsCHECK(!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 (!LinkClass(self, descriptor, klass, interfaces, &new_class)) {// Linking failed.if (!klass->IsErroneous()) {klass->SetStatus(mirror::Class::kStatusError, self);}return nullptr;}self->AssertNoPendingException();CHECK(new_class != nullptr) << descriptor;CHECK(new_class->IsResolved()) << descriptor;Handle<mirror::Class> new_class_h(hs.NewHandle(new_class));/** We send CLASS_PREPARE events to the debugger from here.  The* definition of "preparation" is creating the static fields for a* class and initializing them to the standard default values, but not* executing any code (that comes later, during "initialization").** We did the static preparation in LinkClass.** The class has been prepared and resolved but possibly not yet verified* at this point.*/Dbg::PostClassPrepare(new_class_h.Get());return new_class_h.Get();
}
void ClassLinker::LoadClass(const DexFile& dex_file,const DexFile::ClassDef& dex_class_def,Handle<mirror::Class> klass,mirror::ClassLoader* class_loader) {CHECK(klass.Get() != nullptr);CHECK(klass->GetDexCache() != nullptr);CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);CHECK(descriptor != nullptr);klass->SetClass(GetClassRoot(kJavaLangClass));if (kUseBakerOrBrooksReadBarrier) {klass->AssertReadBarrierPointer();}uint32_t access_flags = dex_class_def.GetJavaAccessFlags();CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U);klass->SetAccessFlags(access_flags);klass->SetClassLoader(class_loader);DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);klass->SetStatus(mirror::Class::kStatusIdx, nullptr);klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));klass->SetDexTypeIndex(dex_class_def.class_idx_);const byte* class_data = dex_file.GetClassData(dex_class_def);if (class_data == nullptr) {return;  // no fields or methods - for example a marker interface}OatFile::OatClass oat_class;if (Runtime::Current()->IsStarted()&& !Runtime::Current()->UseCompileTimeClassPath()&& FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);} else {LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);}
}

LoadClass干的事就是从Oat文件中找到OatClass,然后冲OatClass中拿到class本地机器码去执行。

至此,整个Art加载类的过程就很明显了。

Android art模式解析相关推荐

  1. Android art模式 调试注意的问题

    1.Android art模式调试会出现 部分方法有问题,如果shouldInterceptRequest方法.

  2. Android MVP模式 解析JSON 显示到ListView上

    Android MVP模式 解析JSON 显示到ListView上 有关MVP模式的介绍,这里不作详细解释,稍后会更新MVP设计模式,请等待链接-- 简述本次的主要功能实现: 通过mvp设计模式,(M ...

  3. Android ART模式简介

    新书上市<深入解析Android 5.0系统> 以下内容节选自本书 Android4.4最大的变化就是引入ART模式来代替Dalvik虚拟机.ART是AndroidRuntime的缩写,它 ...

  4. Android ART模式预优化那些事

    Android预优化那些事 Preopt ART Dalvik APK的预优化原理和作用 Android预优化那些事 1.什么是Android预优化 2.Android预优化的原理 3.Android ...

  5. android l art模式,Android ART模式简介

    Android4.4最大的变化就是引入ART模式来代替Dalvik虚拟机.ART是Android Runtime的缩写,它提供了以AOT(Ahead-Of-Time)的方式运行Android应用程序的 ...

  6. Android ART模式

    ART模式是Android Runtime的简称,从Android 4.2 开始出现,在Android 4.2以前,Android 系统的程序运行在Daivik Java的虚拟机上运行,这种运行模式还 ...

  7. android+art模式死机,ADB命令解决切换ART模式后reboot无法进入系统循环卡屏教程

    相信作为原生Android 4.4的一项特色功能,不少同学都会想要体验一下原汁原味ART模式吧.据说在ART模式下Android将不再卡顿,丝丝顺滑的流畅度直逼iOS.当然也有不少同学在把手机切换到A ...

  8. 扭转战局的棋子 安卓4.4 ART模式实测解析

    1传统的Dalvik虚拟机 2013年11月1日,谷歌继续低调发布了Android 4.4和Nexus 5,Android 4.4作为最新的系统版本更换代号为KitKat,但人们发现这个版本的系统似乎 ...

  9. android 5.0.1 libdvm.so,Android逆向进阶—— 脱壳的奥义(基ART模式下的dump)

    本文作者:i春秋作家HAI_ZHU 0×00 前言 市面上的资料大多都是基于Dalvik模式的dump,所以这此准备搞一个ART模式下的dump. Dalvik模式是Android 4.4及其以下采用 ...

最新文章

  1. 用栈实现队列与用队列实现栈
  2. HttpUrlConnection使用详解--转
  3. [20190805]在小程序中使用npm包
  4. 在采用vue-cli Post Get
  5. 【WinForm-无边框窗体】实现Panel移动窗体,没有边框的窗体
  6. 推理速度快千倍!谷歌开源语言模型Transformer-XL
  7. c语言入门级小游戏·飞机(1.0版)| 激发你的编程兴趣(50~100行代码)
  8. 把prn文件输出到网络打印机
  9. python代码实现蜡笔小新
  10. P5144 【蜈蚣】
  11. linux 无法生成图片大小,简单点。表演()在Linux上的ImageJ中生成错误
  12. 秀米怎么添加附件链接,如Word、Excel、Pdf等
  13. iOS开发模拟网络状态差进行调试
  14. 如何bat修改dns
  15. 用python编程解一元二次方程
  16. 感觉自己不会的东西太多了,不知道如何下手?
  17. 使用Python做QQ机器人
  18. 虚拟串口VSPD安装指南
  19. 飞桨领航团AI达人创造营第三课笔记
  20. 使用C#语言编写记事本程序

热门文章

  1. 面试(南京文思创新软件)
  2. android中如何设置页面中的默认返回图标和标题?
  3. 近1万人次参赛 T3出行全国首届车载实况足球冠军赛收官
  4. 心电图多少为正常范围_心电图的各项正常值是多少
  5. 雷婆冬日娜,闭嘴!!
  6. html 作用,作用.html
  7. 修改状态栏字体的颜色
  8. linux grep 带空格的内容,或者搜索多个单词,一段话
  9. 阿里云接口实现发送短信验证码
  10. 计算机名称是pc是什么意思,pc是什么意思