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+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模式解析相关推荐
- Android art模式 调试注意的问题
1.Android art模式调试会出现 部分方法有问题,如果shouldInterceptRequest方法.
- Android MVP模式 解析JSON 显示到ListView上
Android MVP模式 解析JSON 显示到ListView上 有关MVP模式的介绍,这里不作详细解释,稍后会更新MVP设计模式,请等待链接-- 简述本次的主要功能实现: 通过mvp设计模式,(M ...
- Android ART模式简介
新书上市<深入解析Android 5.0系统> 以下内容节选自本书 Android4.4最大的变化就是引入ART模式来代替Dalvik虚拟机.ART是AndroidRuntime的缩写,它 ...
- Android ART模式预优化那些事
Android预优化那些事 Preopt ART Dalvik APK的预优化原理和作用 Android预优化那些事 1.什么是Android预优化 2.Android预优化的原理 3.Android ...
- android l art模式,Android ART模式简介
Android4.4最大的变化就是引入ART模式来代替Dalvik虚拟机.ART是Android Runtime的缩写,它提供了以AOT(Ahead-Of-Time)的方式运行Android应用程序的 ...
- Android ART模式
ART模式是Android Runtime的简称,从Android 4.2 开始出现,在Android 4.2以前,Android 系统的程序运行在Daivik Java的虚拟机上运行,这种运行模式还 ...
- android+art模式死机,ADB命令解决切换ART模式后reboot无法进入系统循环卡屏教程
相信作为原生Android 4.4的一项特色功能,不少同学都会想要体验一下原汁原味ART模式吧.据说在ART模式下Android将不再卡顿,丝丝顺滑的流畅度直逼iOS.当然也有不少同学在把手机切换到A ...
- 扭转战局的棋子 安卓4.4 ART模式实测解析
1传统的Dalvik虚拟机 2013年11月1日,谷歌继续低调发布了Android 4.4和Nexus 5,Android 4.4作为最新的系统版本更换代号为KitKat,但人们发现这个版本的系统似乎 ...
- android 5.0.1 libdvm.so,Android逆向进阶—— 脱壳的奥义(基ART模式下的dump)
本文作者:i春秋作家HAI_ZHU 0×00 前言 市面上的资料大多都是基于Dalvik模式的dump,所以这此准备搞一个ART模式下的dump. Dalvik模式是Android 4.4及其以下采用 ...
最新文章
- 用栈实现队列与用队列实现栈
- HttpUrlConnection使用详解--转
- [20190805]在小程序中使用npm包
- 在采用vue-cli Post Get
- 【WinForm-无边框窗体】实现Panel移动窗体,没有边框的窗体
- 推理速度快千倍!谷歌开源语言模型Transformer-XL
- c语言入门级小游戏·飞机(1.0版)| 激发你的编程兴趣(50~100行代码)
- 把prn文件输出到网络打印机
- python代码实现蜡笔小新
- P5144 【蜈蚣】
- linux 无法生成图片大小,简单点。表演()在Linux上的ImageJ中生成错误
- 秀米怎么添加附件链接,如Word、Excel、Pdf等
- iOS开发模拟网络状态差进行调试
- 如何bat修改dns
- 用python编程解一元二次方程
- 感觉自己不会的东西太多了,不知道如何下手?
- 使用Python做QQ机器人
- 虚拟串口VSPD安装指南
- 飞桨领航团AI达人创造营第三课笔记
- 使用C#语言编写记事本程序