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模式下的dump)