0x00

DexClassLoader和PathClassLoader载入Dex流程一文中,我们分析了dex文件怎样形成了DexFile结构体。本文中解说类载入机制,实际上就是生成ClassObject对象。

我们以DexClassLoader为例。解说类载入机制,PathClassLoader是一样的。

我们在载入类时一般会调用loadClass,那么我们就从loadClass来開始分析。

0x01

DexClassLoader类没有loadClass方法。所以调用的是父类ClassLoader类的loadClass方法,ClassLoader类的loadClass方法位于libcore\luni\src\main\java\java\lang\ClassLoader.java中。

    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className); if (clazz == null) {
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
// Don't want to see this.
} if (clazz == null) {
clazz = findClass(className);
}
} return clazz;
}

DexClassLoader复写了父类ClassLoader的findClass方法。所以调用子类DexClassLoader类的方法findClass。代码位于libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java。

    @Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (VERBOSE_DEBUG)
System.out.println("DexClassLoader " + this
+ ": findClass '" + name + "'"); int length = mFiles.length; for (int i = 0; i < length; i++) {
if (VERBOSE_DEBUG)
System.out.println(" Now searching: " + mFiles[i].getPath()); if (mDexs[i] != null) {
String slashName = name.replace('.', '/');
Class clazz = mDexs[i].loadClass(slashName, this);
if (clazz != null) {
if (VERBOSE_DEBUG)
System.out.println(" found");
return clazz;
}
}
} throw new ClassNotFoundException(name + " in loader " + this);
}

这里调用的是DexFile类的loadClass方法,代码位于libcore\dalvik\src\main\java\dalvik\system\DexFile.java。

    public Class loadClass(String name, ClassLoader loader) {
String slashName = name.replace('.', '/');
return loadClassBinaryName(slashName, loader);
}
    public Class loadClassBinaryName(String name, ClassLoader loader) {
return defineClass(name, loader, mCookie,
null);
//new ProtectionDomain(name) /*DEBUG ONLY*/);
}

defineClass相应的是JNI方法,例如以下:

native private static Class defineClass(String name, ClassLoader loader,
int cookie, ProtectionDomain pd);

还记得在DexClassLoader和PathClassLoader载入Dex流程一文中,openDexFile也是JNI方法。相应的native方法位于dalvik\vm\native\dalvik_system_DexFile.c。

const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
{ "openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)I",
Dalvik_dalvik_system_DexFile_openDexFile },
{ "closeDexFile", "(I)V",
Dalvik_dalvik_system_DexFile_closeDexFile },
{ "defineClass", "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;",
Dalvik_dalvik_system_DexFile_defineClass },
{ "getClassNameList", "(I)[Ljava/lang/String;",
Dalvik_dalvik_system_DexFile_getClassNameList },
{ "isDexOptNeeded", "(Ljava/lang/String;)Z",
Dalvik_dalvik_system_DexFile_isDexOptNeeded },
{ NULL, NULL, NULL },
};

defineClass相应的是Dalvik_dalvik_system_DexFile_defineClass方法。注意defineClass函数传递进来的參数有一个是mCookie,就是在DexClassLoader和PathClassLoader载入Dex流程一文中。openDexFile生成的,利用这个mCookie能够在native层找到openDexFile生成的DexFile结构体。

0x02

Dalvik_dalvik_system_DexFile_defineClass代码位于dalvik\vm\native\dalvik_system_DexFile.c。

static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args,
JValue* pResult)
{
StringObject* nameObj = (StringObject*) args[0];
Object* loader = (Object*) args[1];
int cookie = args[2];
Object* pd = (Object*) args[3];
ClassObject* clazz = NULL;
DexOrJar* pDexOrJar = (DexOrJar*) cookie;
DvmDex* pDvmDex;
char* name;
char* descriptor; name = dvmCreateCstrFromString(nameObj);
descriptor = dvmDotToDescriptor(name);
LOGV("--- Explicit class load '%s' 0x%08x\n", descriptor, cookie);
free(name); if (!validateCookie(cookie))
RETURN_VOID(); if (pDexOrJar->isDex)
pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
else
pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); /* once we load something, we can't unmap the storage */
pDexOrJar->okayToFree = false; clazz = dvmDefineClass(pDvmDex, descriptor, loader);
...... ...... free(descriptor);
RETURN_PTR(clazz);
}

首先通过cookie找到DexOrJar结构体pDexOrJar,然后依据pDexOrJar找到DvmDex结构体pDvmDex。

以下我们来分析核心函数dvmDefineClass,这个用来生成ClassObject。dvmDefineClass。findClassNoInit 方法都位于dalvik\vm\oo\Class.c。

ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
Object* classLoader)
{
assert(pDvmDex != NULL); return findClassNoInit(descriptor, classLoader, pDvmDex);
}
static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
DvmDex* pDvmDex)
{
Thread* self = dvmThreadSelf();
ClassObject* clazz;
bool profilerNotified = false; ......
clazz = dvmLookupClass(descriptor, loader, true);
if (clazz == NULL) {
const DexClassDef* pClassDef; ...... if (pDvmDex == NULL) {
assert(loader == NULL); /* shouldn't be here otherwise */
pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
} else {
pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
} ...... /* found a match, try to load it */
clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
......
if (!dvmAddClassToHash(clazz)) {
......
}
......
}
return clazz;
}

首先调用dvmLookupClass方法,依据目标类的描写叙述符descriptor在系统已载入类中进行查找,如果已对其载入,则返回目标类的ClassObject对象;否则,将对目标类进行载入。

我们如果没有对其载入过,然后调用dexFindClass方法找到DexClassDef结构体。我们首先来看下DexClassDef结构体,代码位于dalvik\vm\oo\Class.c。

typedef struct DexClassDef {
u4 classIdx; /* index into typeIds for this class */
u4 accessFlags;
u4 superclassIdx; /* index into typeIds for superclass */
u4 interfacesOff; /* file offset to DexTypeList */
u4 sourceFileIdx; /* index into stringIds for source file name */
u4 annotationsOff; /* file offset to annotations_directory_item */
u4 classDataOff; /* file offset to class_data_item */
u4 staticValuesOff; /* file offset to DexEncodedArray */
} DexClassDef;

为了方便理解以后的代码,我这里先附上一张图。DexClassDef就是图中最左边的部分class_def_item。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

dexFindClass方法也位于dalvik\vm\oo\Class.c。

const DexClassDef* dexFindClass(const DexFile* pDexFile,
const char* descriptor)
{
const DexClassLookup* pLookup = pDexFile->pClassLookup;
u4 hash;
int idx, mask; hash = classDescriptorHash(descriptor);
mask = pLookup->numEntries - 1;
idx = hash & mask; /*
* Search until we find a matching entry or an empty slot.
*/
while (true) {
int offset; offset = pLookup->table[idx].classDescriptorOffset;
if (offset == 0)
return NULL; if (pLookup->table[idx].classDescriptorHash == hash) {
const char* str; str = (const char*) (pDexFile->baseAddr + offset);
if (strcmp(str, descriptor) == 0) {
return (const DexClassDef*)
(pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
}
} idx = (idx + 1) & mask;
}
}

最后返回值的地方解释下。pDexFile->baseAddr指向dex文件头部。后面加上的是DexClassDef结构体距离dex文件头部的偏移。

返回到findClassNoInit,继续运行loadClassFromDex方法。这是真正生成ClassObject对象的地方。

代码位于dalvik\vm\oo\Class.c。

static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
const DexClassDef* pClassDef, Object* classLoader)
{
ClassObject* result;
DexClassDataHeader header;
const u1* pEncodedData;
const DexFile* pDexFile; assert((pDvmDex != NULL) && (pClassDef != NULL));
pDexFile = pDvmDex->pDexFile; if (gDvm.verboseClass) {
LOGV("CLASS: loading '%s'...\n",
dexGetClassDescriptor(pDexFile, pClassDef));
} 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));
} result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
classLoader); if (gDvm.verboseClass && (result != NULL)) {
LOGI("[Loaded %s from DEX %p (cl=%p)]\n",
result->descriptor, pDvmDex, classLoader);
} return result;
}

dexGetClassData方法用来获取上图中的第二部分class_data_item。

代码位于dalvik\libdex\DexFile.h。

DEX_INLINE const u1* dexGetClassData(const DexFile* pDexFile,
const DexClassDef* pClassDef)
{
if (pClassDef->classDataOff == 0)
return NULL;
return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff);
}

loadClassFromDex0用于生成终于的ClassObject对象。代码位于dalvik\libdex\DexFile.h。

static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
const u1* pEncodedData, Object* classLoader)
{
ClassObject* newClass = NULL;
const DexFile* pDexFile;
const char* descriptor;
int i;

pDexFile = pDvmDex->pDexFile;
descriptor = dexGetClassDescriptor(pDexFile, pClassDef);

/*
* Make sure the aren't any "bonus" flags set, since we use them for
* runtime state.
*/
if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {
LOGW("Invalid file flags in class %s: %04x\n",
descriptor, pClassDef->accessFlags);
return NULL;
}

/*
* Allocate storage for the class object on the GC heap, so that other
* objects can have references to it. We bypass the usual mechanism
* (allocObject), because we don't have all the bits and pieces yet.
*
* Note that we assume that java.lang.Class does not override
* finalize().
*/
/* TODO: Can there be fewer special checks in the usual path?

*/
assert(descriptor != NULL);
if (classLoader == NULL &&
strcmp(descriptor, "Ljava/lang/Class;") == 0) {
assert(gDvm.classJavaLangClass != NULL);
newClass = gDvm.classJavaLangClass;
} else {
size_t size = classObjectSize(pHeader->staticFieldsSize);
newClass = (ClassObject*) dvmMalloc(size, ALLOC_DEFAULT);
}
if (newClass == NULL)
return NULL;

DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
dvmSetClassSerialNumber(newClass);
newClass->descriptor = descriptor;
assert(newClass->descriptorAlloc == NULL);
newClass->accessFlags = pClassDef->accessFlags;
dvmSetFieldObject((Object *)newClass,
offsetof(ClassObject, classLoader),
(Object *)classLoader);
newClass->pDvmDex = pDvmDex;
newClass->primitiveType = PRIM_NOT;
newClass->status = CLASS_IDX;

/*
* Stuff the superclass index into the object pointer field. The linker
* pulls it out and replaces it with a resolved ClassObject pointer.
* I'm doing it this way (rather than having a dedicated superclassIdx
* field) to save a few bytes of overhead per class.
*
* newClass->super is not traversed or freed by dvmFreeClassInnards, so
* this is safe.
*/
assert(sizeof(u4) == sizeof(ClassObject*)); /* 32-bit check */
newClass->super = (ClassObject*) pClassDef->superclassIdx;

/*
* Stuff class reference indices into the pointer fields.
*
* The elements of newClass->interfaces are not traversed or freed by
* dvmFreeClassInnards, so this is GC-safe.
*/
const DexTypeList* pInterfacesList;
pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef);
if (pInterfacesList != NULL) {
newClass->interfaceCount = pInterfacesList->size;
newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader,
newClass->interfaceCount * sizeof(ClassObject*));

for (i = 0; i < newClass->interfaceCount; i++) {
const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i);
newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx;
}
dvmLinearReadOnly(classLoader, newClass->interfaces);
}

/* load field definitions */

/*
* Over-allocate the class object and append static field info
* onto the end. It's fixed-size and known at alloc time. This
* seems to increase zygote sharing. Heap compaction will have to
* be careful if it ever tries to move ClassObject instances,
* because we pass Field pointers around internally. But at least
* now these Field pointers are in the object heap.
*/

if (pHeader->staticFieldsSize != 0) {
/* static fields stay on system heap; field data isn't "write once" */
int count = (int) pHeader->staticFieldsSize;
u4 lastIndex = 0;
DexField field;

newClass->sfieldCount = count;
for (i = 0; i < count; i++) {
dexReadClassDataField(&pEncodedData, &field, &lastIndex);
loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
}
}

if (pHeader->instanceFieldsSize != 0) {
int count = (int) pHeader->instanceFieldsSize;
u4 lastIndex = 0;
DexField field;

newClass->ifieldCount = count;
newClass->ifields = (InstField*) dvmLinearAlloc(classLoader,
count * sizeof(InstField));
for (i = 0; i < count; i++) {
dexReadClassDataField(&pEncodedData, &field, &lastIndex);
loadIFieldFromDex(newClass, &field, &newClass->ifields[i]);
}
dvmLinearReadOnly(classLoader, newClass->ifields);
}

/*
* Load method definitions. We do this in two batches, direct then
* virtual.
*
* If register maps ha

DexClassLoader和PathClassLoader类载入机制的更多相关文章

  1. JVM系列文章(四):类载入机制

    作为一个程序猿,只知道怎么用是远远不够的. 起码,你须要知道为什么能够这么用.即我们所谓底层的东西. 那究竟什么是底层呢?我认为这不能一概而论.以我如今的知识水平而言:对于Web开发人员,TCP/IP ...

  2. 深入研究Java类载入机制

    深入研究Java类载入机制   类载入是Java程序运行的第一步,研究类的载入有助于了解JVM运行过程,并指导开发人员採取更有效的措施配合程序运行. 研究类载入机制的第二个目的是让程序能动态的控制类载 ...

  3. Java虚拟机的类载入机制

    Java虚拟机类载入过程是把Class类文件载入到内存.并对Class文件里的数据进行校验.转换解析和初始化,终于形成能够被虚拟机直接使用的java类型的过程. 在载入阶段,java虚拟机须要完毕下面 ...

  4. Jetty 类载入问题处理

    前几日使用 Jetty (9.2)部署公司一个 web 项目,这个项目原本部署在 Tomcat server上,一切正常,可是部署到 Jetty 后,启动报错.关键错误信息为"java.la ...

  5. DexClassLoader和PathClassLoader载入Dex流程

    0x00 在上一篇文章apk安装和优化原理,在最后我们分析了DexClassLoader和PathClassLoader的构造函数的不同. PathClassLoader最后调用的是new DexFi ...

  6. Tomcat类载入器机制(Tomcat源代码解析六)

    要说Tomcat的Classloader机制,我们还得从Bootstrap開始.在BootStrap初始化的时候.调用了org.apache.catalina.startup.Bootstrap#in ...

  7. DexClassLoader和PathClassLoader

    Android的ClassLoader体系 在Android中可以跟java一样实现动态加载jar,但是Android使用Dalvik VM,不能直接加载java打包jar的byte code,需要通 ...

  8. Java类载入器(二)——自己定义类载入器

      用户定制自己的ClassLoader能够实现以下的一些应用: 自己定义路径下查找自己定义的class类文件,或许我们须要的class文件并不总是在已经设置好的Classpath以下,那么我们必须想 ...

  9. 深入研究Java类装载机制

    目录 1.为什么要研究java类装在机制? 2.了解类装载机制,对于我们在项目开发中有什么作用? 3.装载实现细节. 4.总结 一.为什么药研究Java类装载机制 java类加载机制,便于我们使用自定 ...

随机推荐

  1. 长脖子鹿省选模拟赛 [LnOI2019SP]快速多项式变换(FPT)

    本片题解设计两种解法 果然是签到题... 因为返回值问题T了好久... 第一眼:搜索大水题? 然后...竟然A了 #include<cstdio> #include<queue> ...

  2. 5.7 Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。-mybatis-generator将数据库表反向生成对应的实体类及基于mybatis的mapper接口和xml映射文件(类似代码生成器)

    一. liquibase 使用说明 功能概述:通过xml文件规范化维护数据库表结构及初始化数据. 1.配置不同环境下的数据库信息 (1)创建不同环境的数据库. (2)在resource/liquiba ...

  3. SQLCE本地数据库

    SQLCE是一个标准得关系数据库,可以使用 LINQ 和DateContext来处理本地数据库数据库. 使用SQLCE 要在代码中使用本地数据库功能,需要添加以下命名空间 : using System ...

  4. 快速录入快递地址API接口实现

    电商.ERP等行业下单环节极其重要,如何提高下单的效率已经成为首要问题.快速下单对于客户来说,为提前发货争取了时间:对于卖家来说,提高了库存周转率及利用率.快速下单的接口实现,需要解决如下几个问题:1 ...

  5. jQuery学习笔记之DOM操作、事件绑定(2)

    jQuery学习笔记之DOM操作.事件绑定(2) --------------------学习目录------------------------ 4.DOM操作 5.事件绑定 源码地址: https ...

  6. 数据库 'tempdb' 的事务日志已满。若要查明无法重用日志中的空间的原因

    最常的做法: --1.清空日志 DUMP TRANSACTION tempdb WITH NO_LOG --2.截断事务日志: BACKUP LOG tempdb WITH NO_LOG --3.收缩 ...

  7. 安装rails卡住很慢 出现302 Moved Temporarily

    在MAC上安装rails的时候,使用命令$ gem install rails 发现一直没响应,使用$ gem install rails-V命令发现,安装会在中间卡住,出现302 Moved Tem ...

  8. element-ui按需引入

    { "name": "vue-test2", "description": "A Vue.js project", &q ...

  9. python tips:匿名函数lambda

    lambda用于创建匿名函数,下面两种函数定义方式等价. f = lambda x: x + 2 def f(x): return x + 2 立刻执行的匿名函数 (lambda x: print(x ...

  10. luogu P1856 [USACO5.5]矩形周长Picture 扫描线 + 线段树

    Code: #include<bits/stdc++.h> #define maxn 200007 #define inf 100005 using namespace std; void ...