在Android系统中,应用程序进程都是由Zygote进程孵化出来的,而Zygote进程是由Init进程启动的。Zygote进程在启动时会创建一个Dalvik虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的Dalvik虚拟机实例。
每个android应用程序都是运行在虚拟机实例上的,而其中dex的加载就比如离开不了虚拟机的初始化。
JNI_CreateJavaVM
位于/dalvik/vm/Jni.cpp.
创建了一个虚拟机之后调用 dvmStartup 初始化虚拟机。
1
| std::string status = dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
|
dvmStartup
位于/dalvik/vm/Init.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13
| std::string dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized, JNIEnv* pEnv) { ... if (!dvmClassStartup()) { //初始化文件的结构体引用 return "dvmClassStartup failed"; } ... if (!dvmFindRequiredClassesAndMembers()) { //初始化成员、类的引用 return "dvmFindRequiredClassesAndMembers failed"; } ... }
|
dvmClassStartup
位于/dalvik/vm/oo/Class.cpp
处理启动类路径,打开特定的dex/jar文件,可能会进行一些优化。
1 2 3 4 5 6
| bool dvmClassStartup() { ... processClassPath(gDvm.bootClassPathStr, true); ... }
|
processClassPath
将一些文件路径转化为ClassPathEntry结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap) { ... cp = mangle; idx = 0; while (cp < end) { //循环遍历pathStr ... ClassPathEntry tmp; tmp.kind = kCpeUnknown; tmp.fileName = strdup(cp); tmp.ptr = NULL; cpe[idx].kind = kCpeLastEntry; cpe[idx].fileName = NULL; cpe[idx].ptr = NULL; //为每个文件准备一个cpe if (!prepareCpe(&tmp, isBootstrap)) { /* drop from list and continue on */ free(tmp.fileName); } else { /* copy over, pointers and all */ cpe[idx] = tmp; idx++; } } cp += strlen(cp) +1; } ... return cpe; }
|
prepareCpe
位于/dalvik/vm/oo/Class.cpp.
根据传进的文件名判断这是一个什么样的文件,提取出有用的信息,存入ClassPathEntry结构体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| static bool prepareCpe(ClassPathEntry* cpe, bool isBootstrap) { ... char suffix[10]; getFileNameSuffix(cpe->fileName, suffix, sizeof(suffix)); //根据后缀名判断文件类型 if ((strcmp(suffix, "jar") == 0) || (strcmp(suffix, "zip") == 0) || (strcmp(suffix, "apk") == 0)) { JarFile* pJarFile = NULL; if (dvmJarFileOpen(cpe->fileName, NULL, &pJarFile, isBootstrap) == 0) { cpe->kind = kCpeJar; cpe->ptr = pJarFile; return true; } } else if (strcmp(suffix, "dex") == 0) { RawDexFile* pRawDexFile = NULL; if (dvmRawDexFileOpen(cpe->fileName, NULL, &pRawDexFile, isBootstrap) == 0) { cpe->kind = kCpeDex; cpe->ptr = pRawDexFile; return true; } } else { ALOGE("Unknown type suffix '%s'", suffix); } ... }
|
dvmRawDexFileOpen
位于/dalvik/vm/RawDexFile.cpp.
打开一个原生dex文件,优化,加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName, RawDexFile** ppRawDexFile, bool isBootstrap) { ... dexFd = open(fileName, O_RDONLY); //打开文件 if (dexFd < 0) goto bail; if (odexOutputName == NULL) { //构造一个dex cache name cachedName = dexOptGenerateCacheFileName(fileName, NULL); if (cachedName == NULL) goto bail; } ... //打开cache中的文件,如果没有会新建一个,填充头部,验证dependencies optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime, adler32, isBootstrap, &newFile, /*createIfMissing=*/true); //新生成一个优化的dex if (newFile) { ... if (result) { //内部执行了system/bin目录下的dexopt文件 result = dvmOptimizeDexFile(optFd, dexOffset, fileSize, fileName, modTime, adler32, isBootstrap); } ... } if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) { ALOGI("Unable to map cached %s", fileName); goto bail; } ... *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile)); (*ppRawDexFile)->cacheFileName = cachedName; (*ppRawDexFile)->pDvmDex = pDvmDex; //新文件的指针 cachedName = NULL; // don't free it below result = 0; ... }
|
dvmFindRequiredClassesAndMembers
位于/dalvik/vm/InitRefs.cpp .
遍历类列表和成员列表,将他们的引用存入全局遍历gDvm中以便日后引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| /* * Look up the set of classes and members used directly by the VM, * storing references to them into the globals instance. See * Globals.h. This function is exposed so that dex optimization may * call it (while avoiding doing other unnecessary VM initialization). * * The function returns a success flag (true == success). */ bool dvmFindRequiredClassesAndMembers() { /* * Note: Under normal VM use, this is called by dvmStartup() * in Init.c. For dex optimization, this is called as well, but in * that case, the call is made from DexPrepare.c. */ return initClassReferences() && initFieldOffsets() && initConstructorReferences() && initDirectMethodReferences() && initVirtualMethodOffsets() && initFinalizerReference() && verifyStringOffsets(); }
|
仅以initClassReferences为例。
initClassReferences
位于/dalvik/vm/InitRefs.cpp .
初始化一下核心类、数组类、异常类的引用。
1 2 3 4 5 6 7 8 9 10
| static bool initClassReferences() { ... int i; //初始化类的引用 for (i = 0; classes[i].ref != NULL; i++) { if (!initClassReference(classes[i].ref, classes[i].name)) { return false; } } return true; }
|
initClassReference
位于/dalvik/vm/InitRefs.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static bool initClassReference(ClassObject** pClass, const char* name) { ClassObject* result; assert(*pClass == NULL); if (name[0] == '[') { //基本类类型的数组 result = dvmFindArrayClass(name, NULL); } else { //系统类的引用 result = dvmFindSystemClassNoInit(name); } if (result == NULL) { ALOGE("Could not find essential class %s", name); return false; } *pClass = result; return true; }
|
dvmFindSystemClassNoInit
位于/dalvik/vm/oo/Class.cpp.
1 2 3 4 5 6 7 8 9 10
| /* * Find the named class (by descriptor), searching for it in the * bootclasspath. * * On failure, this returns NULL with an exception raised. */ ClassObject* dvmFindSystemClassNoInit(const char* descriptor) { return findClassNoInit(descriptor, NULL, NULL); }
|
findClassNoInit
位于/dalvik/vm/oo/Class.cpp
通过文件描述符找到相应的类,如果类没有加载,我们就去加载并链接,但是不会执行.
这里会返回一个是通过加载得到的类还是用的已有的类定义的标识,以防在同一个class loader中被加载两次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| static ClassObject* findClassNoInit(const char* descriptor, Object* loader, DvmDex* pDvmDex) { //在已经加载的类列表中查找(hash) clazz = dvmLookupClass(descriptor, loader, true); if (clazz == NULL) { //如果没有找到相应的类切没有传进dex文件的参数,就在dex列表中查找,找到之后加载dex文件中的类 const DexClassDef* pClassDef; ... if (pDvmDex == NULL) { assert(loader == NULL); /* shouldn't be here otherwise */ pDvmDex = searchBootPathForClass(descriptor, &pClassDef); //在dex列表中查找 } else { //在提供的dex文件中查找 pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor); } ... /* found a match, try to load it */ clazz = loadClassFromDex(pDvmDex, pClassDef, loader); //加载dex! ... } }
|
loadClassFromDex
位于/dalvik/vm/oo/Class.cpp.
从指定的dex文件中加载指定的类。此函数相当于loadClass()+defineClass().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| /* * Try to load the indicated class from the specified DEX file. * * This is effectively loadClass()+defineClass() for a DexClassDef. The * loading was largely done when we crunched through the DEX. * * Returns NULL on failure. If we locate the class but encounter an error * while processing it, an appropriate exception is thrown. */ static ClassObject* loadClassFromDex(DvmDex* pDvmDex, const DexClassDef* pClassDef, Object* classLoader) { ... pDexFile = pDvmDex->pDexFile; //通过pClassDef得到class_data_item指针 pEncodedData = dexGetClassData(pDexFile, pClassDef); if (pEncodedData != NULL) { dexReadClassDataHeader(&pEncodedData, &header); } else { // Provide an all-zeroes header for the rest of the loading. memset(&header, 0, sizeof(header)); } //通过头部数据和class_data_item得到其他的数据 result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData, classLoader); return result; }
|
loadClassFromDex0
位于/dalvik/vm/oo/Class.cpp.
通过头部数据和class_data_item,返回一个class的全部数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| static ClassObject* loadClassFromDex0(DvmDex* pDvmDex, const DexClassDef* pClassDef, const DexClassDataHeader* pHeader, const u1* pEncodedData, Object* classLoader) { ClassObject* newClass = NULL; descriptor = dexGetClassDescriptor(pDexFile, pClassDef); newClass->descriptor = descriptor; //描述符 SET_CLASS_FLAG(newClass, pClassDef->accessFlags); //权限标识 newClass->pDvmDex = pDvmDex; newClass->primitiveType = PRIM_NOT; newClass->status = CLASS_IDX; ... newClass->super = (ClassObject*) pClassDef->superclassIdx; //父类 .... .... newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef); //源文件 /* caller must call dvmReleaseTrackedAlloc */ return newClass; }
|
总结
虚拟机创建后开始初始化,其中一步就是初始化类的引用,类成员的引用。
在初始化文件结构体的引用时,虚拟机根据全局变量gDvm中的启动类路径来为每个类生成一个ClassPathEntry的结构体引用,处理的过程中为压缩文件/dex调用了不同的函数,
处理dex文件时顺便对齐进行了优化,生成了odex文件。(当然4.4之后由于采用了ART代替dalvik,优化后的文件格式应为elf)。
在初始化类中数据的引用时,先是在已经加载的类列表中查找,如果没有的话再逐个的对dex进行解剖查找,找到相应的dex后,加载这个dex文件解析全部数据。
Reference
Android在线源码
Dalvik虚拟机的启动过程分析
Dalvik的BOOTCLASSPATH和dexopt流程