美文网首页
类的加载

类的加载

作者: Wi1ls努力努力再努力 | 来源:发表于2019-05-31 14:56 被阅读0次

加载的类在 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

相关文章

  • 第一章 类加载过程

    要点 类加载过程 类加载器 一、类加载过程 1.类的加载过程 类的加载 .class文件过程分为:加载---->连...

  • 深入理解jvm类加载机制

    1.什么是类加载? 类加载机制一个很大的体系,包括类加载的时机,类加载器,类加载时机。 1.1类加载过程 加载器加...

  • java基础知识之java类加载器

    1. 什么是类加载器 类加载器就是用来加载类的东西!类加载器也是一个类:ClassLoader 类加载器可以被加载...

  • 《深入理解JVM虚拟机》读书笔记-类加载器&Java模块化系统

    类加载器 一.类加载器 1.1 类与类加载器 类加载器的定义: Java虚拟机设计团队有意把 类加载阶段中 的“ ...

  • JVM类加载入门

    一 类加载顺序 class类加载-->验证-->准备--->解析--->初始化 class类加载:通过类加载器加载...

  • 学习笔记 | JAVA的反射(二)

    利用反射机制动态加载类 、获取类的方法、获取类的属性 编译时刻加载类是静态加载类,运行时加载类是动态加载类 正常创...

  • jvm类加载器详解和如何打破双亲委派机制

    类加载过程: 项目启动的时候,并不是加载项目中的所有类,是在使用的时候加载,类加载器加载类的时候首先加载父类,所以...

  • JVM - ClassLoader

    1. 概述 类加载器实际定义了类的namespace。 2.类加载方式之当前类加载器和指定类加载器 类的加载只有两...

  • java-类加载机制

    类的加载机制 主要关注点: 什么是类的加载 类的生命周期 类加载器 双亲委派模型 什么是类的加载 类的加载指的是将...

  • java类加载器及其原理

    java类加载器 : java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器) ...

网友评论

      本文标题:类的加载

      本文链接:https://www.haomeiwen.com/subject/mhkitctx.html