加载的类在 Dalvik 中是以 struct ClassObject 的形式存在的。
//@dalvik/vm/oo/Object.h
struct ClassObject : Object {
u4 instanceData[CLASS_FIELD_SLOTS];
const char* descriptor;//类描述符
char* descriptorAlloc;
u4 accessFlags;//访问标示符
u4 serialNumber;//系列号
DvmDex* pDvmDex;//指向所属的 Dex 文件
ClassStatus status;//类状态标志 state of class initialization
ClassObject* verifyErrorClass;//错误处理
u4 initThreadId;//初始化进程 ID
size_t objectSize;
ClassObject* elementClass;//元素类 arrays only
int arrayDim;//数组维数 arrays only
PrimitiveType primitiveType;//原始类型
ClassObject* super;//超类
Object* classLoader;//类装载器 defining class loader, or NULL for the "bootstrap" system loader
InitiatingLoaderList initiatingLoaderList;//initiating class loader list
int interfaceCount;//接口数目
ClassObject** interfaces;//对象接口
int directMethodCount;//直接方法数
Method* directMethods;//指向直接方法区
int virtualMethodCount;//虚方法数
Method* virtualMethods;//指向虚方法区
//Virtual method table, for use by "involke-virtual". The
//vtable from superclass is copied in, and virtual methods from
//our class either replace those from the super or are appended;
//与 Hotspot 虚拟机类似,子类会 copy 一份父类的虚方法,并且重写重写方法的指针
int vtableCount;
Method** vtable;
//Interface table, one entry per interface supported by this class.
int iftableCount;//接口表数目
InterfaceEntry* iftable;//指向接口表
int ifviPoolCount;//常量池数目
int* ifviPool;//指向常量池
//instance fields
int ifieldCount;//实例字段数目
int ifieldRefCount;//引用字段数目
InstField* ifields;//实例字段数目
u4 refOffsets;//字段区偏移
const char* sourceFile;//源文件名
int sfieldCount;//静态字段数目
StaticField sfields[ ];//静态字段指针
}
接下里看比较重要 Method 结构
struct Method{
ClassObject* clazz;
u4 accessFlags;
//for concrete virtual methods, this is the offset of the method in "vtable";
//For abstract methods in an interface class, this is the offset of the method in "iftable[n]->methodIndexArray"
u2 methodIndex;
u2 registersSize;
u2 outSize;
u2 insSize;
//method name
const char* name;
//方法原型 Method prototype descriptor string(return and argument types)
DexProto prototype;
//short-form method descriptor string短方法原型
const char* shorty;
//the actual code
const u2* insns;
int jniArgInfo;
DalvikBridgeFunc nativeFunc;
bool fastJni;
bool noRef;
bool shouldTrace;
const RegisterMap* registerMap;
bool inProfile;
}
另外一个特别重要的作为ClassObject 缓存的哈希表 DexClassLookup
@dalvik/libdex/DexFile.h
struct DexClassLookup{
//表的总大小
int size;
//表项入口数量,为了减少哈希碰撞蔡旭 dexRoundUpPower2()算法计算
//在源码体现是比如实际数量是 5,则先 x2 获得 10,再向上取 2 的幂。即 16
int numEntries;
struct{
//类描述符的哈希
u4 classDescriptorHash;
//类描述符在 dex 文件的偏移
int classDescriptorOffset;
//类定义在 dex 文件的偏移
int classDefOffset;
}table[1];
};
看一波类加载的调用链
findClass(String name)@BaseDexClassLoader.java
↓
findClass(String name)@DexPathList.java
↓
loadClassBinaryName(String name, ClassLoader loader )@DexFile.java
↓
native static Class defineClass(String name, ClassLoader loader, in cookie) @DexFile.java[这里的 cookie 就是前文解析的 DexOrJar]
↓
static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args, JValue* pResult) @ dalvik/vm/native/dalvik_system_DexFile.cpp
↓
ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descritor, Object* classLoader) @ dalvik/vm/oo/Class.cpp
↓
static ClassObject* findClassNoInit(const char* descriptor, Object* loader, DvmDex* pDvmDex) @ dalvik/vm/oo/Class.cpp
--
重点解析 static ClassObject* findClassNoInit()@Class.cpp
static ClassObect* findClassNoInit(const char* descriptor, Object* loader, DvmDex* pDvmDex){
Thread* self = dvmThreadSelf( );
ClassObject* clazz;
...
//从缓存加载
clazz = dvmLookupClass(descriptor, loader, true);
if(clazz == NULL){
//缓存加载失败,说明还未加载
//类在 Dex 中的数据结构 class_def_item
const DexClassDef* pClassDef;
if(pDvmDex == NULL){
//说明加载的是系统类
pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
}else{
//加载的是用户类,重点 1
pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
}
...
//根据 DevmDex, ClassDef,和 loader 加载 ClasObject
clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
...
dvmAddClassToHash(clazz);
}
}
//@DexFile.h
// Direct-mapped "class_def_item".
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 */
};
上面的重点有两个,一个是根据DvmDex和 descriptor 获取 DexClassRef,一个是根据 DvmDex,ClassRef,loader,获得ClassObject
- 1
//@DexFile.cpp
const DexClassDef* dexFindClass(const DexFile* pDexFile, const char*descriptor){
const DexClassLookup* pLookup = pDexFile->pClassLookup;
u4 hash;
int idx, mask;
//根据 descriptor 获取 has 值;
hash = classDescriptorHash(descriptor);
mask = pLookup->numEntries-1;
//根据根据哈希模运算获得索引
idx = has&mask;
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;
}
}
可以看到,如果发生了哈希碰撞,则会取下一个位置进行判断。关于 ClassLookup 的是在 loadAllClassed( )@DexPrepare.cpp 中
- 2
static ClassObject* loadClassFromDex(DvmDex* pDvmDex, const DexClassDef* pClassDef, Object* classloader){
ClassObject* result;
DexClassDataHeader header;
const u1* pEncodedData;
const DexFile* pDexFile;
pDexFile = pDvmDex->pDexFile;
...
result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData);
}
static ClassObject* loadClassFromDex0(DvmDex* pDvmDex, const DexClassDef* pClassDef, const DexClassDataHeader* pHeader, const u1* pEncodedData, Object* classLoader){
ClassObject* newClass = NULL;//目标类实例对象
const DexFile* pDexFile;//存储类对应的 DexFile 对象
const char* descriptor;//类描述符
pDexFile = pDvmDex->pDexFile;
descriptor = dexGetClassDescriptor(pDexFile, pClassDef);
if(classLoader = NULL && ...){
newClass = gDvm.classJavaLangClass;
}else{
//获取对象实例大小并且在内存申请空间
size_t size = classObjectSize(pHeader->staticFieldsSize);
newClass = (ClassObject*) dvmMalloc(size, ALLOC_NON_MOVING);
}
...
//设置字段对象
dvmSetFieldObject((Object *)newClass, OFFSETOF_MEMBER(ClassObject, classLoader), (Object *)classLoader);
newClass->pDvmDex = pDvmDex;
newClass->primitiveType = PRIM_NOT;
newClass->status = CLASS_IDX;
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);
}
//加载静态字段
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);
}
...
//加载类方法
if (pHeader->directMethodsSize != 0) {
int count = (int) pHeader->directMethodsSize;
u4 lastIndex = 0;
DexMethod method;
newClass->directMethodCount = count;
newClass->directMethods = (Method*) dvmLinearAlloc(classLoader,
count * sizeof(Method));
//遍历加载类方法
for (i = 0; i < count; i++) {
dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
loadMethodFromDex(newClass, &method, &newClass->directMethods[i]);
if (classMapData != NULL) {
const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
newClass->directMethods[i].registerMap = pMap;
/* TODO: add rigorous checks */
assert((newClass->directMethods[i].registersSize+7) / 8 ==
newClass->directMethods[i].registerMap->regWidth);
}
}
}
dvmLinearReadOnly(classLoader, newClass->directMethods);
}
//加载虚方法
if (pHeader->virtualMethodsSize != 0) {
int count = (int) pHeader->virtualMethodsSize;
u4 lastIndex = 0;
DexMethod method;
newClass->virtualMethodCount = count;
newClass->virtualMethods = (Method*) dvmLinearAlloc(classLoader,
count * sizeof(Method));
//遍历加载虚方法
for (i = 0; i < count; i++) {
dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
loadMethodFromDex(newClass, &method, &newClass->virtualMethods[i]);
if (classMapData != NULL) {
const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
newClass->virtualMethods[i].registerMap = pMap;
/* TODO: add rigorous checks */
assert((newClass->virtualMethods[i].registersSize+7) / 8 ==
newClass->virtualMethods[i].registerMap->regWidth);
}
}
}
dvmLinearReadOnly(classLoader, newClass->virtualMethods);
}
//保存源文件信息
newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);
}
以上,像一些 Method 具体是如何解析,可以参见loadMethodFromDex()@Class.cpp。其他的解析也可以自行深入查看。
解析完毕后,将 clazz 加入到全局 hash表
bool dvmAddClassToHash(ClassObject* clazz){
void* found;
u3 hash;
hash = dvmComputeUtf8Hash(clazz->descriptor);
//加锁
dvmHashTableLock(gDvm.loadedClasses);
//@dalvik/vm/Hash.cpp 具体不分析了,自行研究
found = dvmHashTableLookup(gDvm.loadedClasses, hash, clazz, hashcmpClassByClass, true);
//释放锁
//dvmHashTableUnlock(gDvm.loadedClasses);
}
其实在上面去加载 class 时,会先从全局 hash 中搜索。
ClassObject* dvmLookupClass(const char* descriptor, Object* loader, bool unprepOkay){
...
found = dvmHashTableLookup(gDvm.loaderClasses, hash, &crit, hashcmpClassByCrit, false);
...
}
看完后自己是有个疑问的,在添加到全局 hash 的时候,添加的是 ClassObject 对象,但是在搜索的时候确实以一个ClassMatchCriteria去寻找的。
struct ClassMatchCriteria {
const char* descriptor;
Object* loader;
}
再来看 dvmHashTableLookup()是如何判断搜索目标一致性的,中间有一行
//Hash.cpp
(*cmpFunc)(pEntry->data, item) == 0
而 cmpFunc 是一个函数指针,而我们知道 pEntry->data 就是 ClassObject 对象,而 item 就是 ClassMatchCriteria 对象,现在来看 cmpFunc 这个函数指针。
//class.cpp
static int hashCmpClassByCrit(const void* vclazz, const void* vcrit){
const ClassObject* clazz = (const ClassObject*) vclazz;
const ClassMatchCriteria* pCrit = (const ClassMatchCriteria*) vcrit;
bool match;
match = (strcmp(clazz->descriptor, pCrit->descriptor) == 0 &&
(clazz->classLoader == pCrit->loader ||
(pCrit->loader !=NULL && dvmLoaderInInitiatingList(clazz, pCrit->loader))));
return !match;
}
于是从源码可以看到,一个类在 dvm 中装载,是由其类的签名和加载类的类加载器决定的。如果有一个不一致,则认识是两个不同的ClassObject。
双亲委派模型规定了加载一个类的顺序,所谓的加载器对于 DVM 来说地位都是一样的,可以认为是与 类的descriptor共同作为 key 来标志一个 ClassObject
网友评论