類的加載

加載的類在 Dalvik 中是以 struct ClassObject 的形式存在的。

//@dalvik/vm/oo/Object.h
struct ClassObject : Object {
  u4 instanceData[CLASS_FIELD_SLOTS];
  
  const char* descriptor;//類描述符
  char* descriptorAlloc;
  
  u4 accessFlags;//訪問標(biāo)示符

  u4 serialNumber;//系列號

  DvmDex* pDvmDex;//指向所屬的 Dex 文件
  
  ClassStatus status;//類狀態(tài)標(biāo)志 state of class initialization

  ClassObject* verifyErrorClass;//錯(cuò)誤處理

  u4 initThreadId;//初始化進(jìn)程 ID

  size_t objectSize;

  ClassObject* elementClass;//元素類 arrays only

  int arrayDim;//數(shù)組維數(shù) 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;//接口數(shù)目
  ClassObject** interfaces;//對象接口
  
  int directMethodCount;//直接方法數(shù)
  Method* directMethods;//指向直接方法區(qū)

  int virtualMethodCount;//虛方法數(shù)
  Method* virtualMethods;//指向虛方法區(qū)

  //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 虛擬機(jī)類似,子類會(huì) copy 一份父類的虛方法巷波,并且重寫重寫方法的指針
  int vtableCount;
  Method** vtable;
  
  //Interface table, one entry per interface supported by this class.
  int iftableCount;//接口表數(shù)目
  InterfaceEntry* iftable;//指向接口表

  int ifviPoolCount;//常量池?cái)?shù)目
  int* ifviPool;//指向常量池

  //instance fields
  int ifieldCount;//實(shí)例字段數(shù)目
  int ifieldRefCount;//引用字段數(shù)目
  InstField* ifields;//實(shí)例字段數(shù)目

  u4 refOffsets;//字段區(qū)偏移

  const char* sourceFile;//源文件名

  int sfieldCount;//靜態(tài)字段數(shù)目
  StaticField sfields[ ];//靜態(tài)字段指針
}

接下里看比較重要 Method 結(jié)構(gòu)

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;
}

另外一個(gè)特別重要的作為ClassObject 緩存的哈希表 DexClassLookup
@dalvik/libdex/DexFile.h

struct DexClassLookup{
  //表的總大小
  int size;
  //表項(xiàng)入口數(shù)量,為了減少哈希碰撞蔡旭 dexRoundUpPower2()算法計(jì)算
  //在源碼體現(xiàn)是比如實(shí)際數(shù)量是 5,則先 x2 獲得 10,再向上取 2 的冪豁护。即 16
  int numEntries;
  struct{
    //類描述符的哈希
    u4 classDescriptorHash;
    //類描述符在 dex 文件的偏移
    int classDescriptorOffset;
    //類定義在 dex 文件的偏移
    int classDefOffset;
  }table[1];
};

看一波類加載的調(diào)用鏈

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

--

重點(diǎn)解析 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 中的數(shù)據(jù)結(jié)構(gòu) class_def_item
    const DexClassDef* pClassDef;
    if(pDvmDex == NULL){
    //說明加載的是系統(tǒng)類
      pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
    }else{
     //加載的是用戶類,重點(diǎn) 1
     pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
    }
    ...
    //根據(jù) 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 */
};

上面的重點(diǎn)有兩個(gè)欲间,一個(gè)是根據(jù)DvmDex和 descriptor 獲取 DexClassRef,一個(gè)是根據(jù) 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;
  //根據(jù) descriptor 獲取 has 值楚里;
  hash = classDescriptorHash(descriptor); 
  mask = pLookup->numEntries-1;
  //根據(jù)根據(jù)哈希模運(yùn)算獲得索引
  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;  
  }
 }

可以看到,如果發(fā)生了哈希碰撞猎贴,則會(huì)取下一個(gè)位置進(jìn)行判斷班缎。關(guān)于 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;//目標(biāo)類實(shí)例對象
  const DexFile* pDexFile;//存儲(chǔ)類對應(yīng)的 DexFile 對象
  const char* descriptor;//類描述符
  
  pDexFile = pDvmDex->pDexFile;
  descriptor = dexGetClassDescriptor(pDexFile, pClassDef);
  
  if(classLoader = NULL && ...){
    newClass = gDvm.classJavaLangClass;
  }else{
    //獲取對象實(shí)例大小并且在內(nèi)存申請空間
     size_t size = classObjectSize(pHeader->staticFieldsSize);
     newClass = (ClassObject*) dvmMalloc(size, ALLOC_NON_MOVING);
  }
  ...
  //設(shè)置字段對象
  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) {
      //接口數(shù)量
      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);
    }

//加載靜態(tài)字段
    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;
        //靜態(tài)字段名
        newClass->sfieldCount = count;
        //遍歷加載字段
        for (i = 0; i < count; i++) {
            dexReadClassDataField(&pEncodedData, &field, &lastIndex);
            loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
        }
    }
    //實(shí)例字段
    if (pHeader->instanceFieldsSize != 0) {
        int count = (int) pHeader->instanceFieldsSize;
        u4 lastIndex = 0;
        DexField field;

        newClass->ifieldCount = count;
        //遍歷加載實(shí)例字段
        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);
}

其實(shí)在上面去加載 class 時(shí),會(huì)先從全局 hash 中搜索苛败。

ClassObject* dvmLookupClass(const char* descriptor, Object* loader, bool unprepOkay){
  ...
  found = dvmHashTableLookup(gDvm.loaderClasses, hash, &crit, hashcmpClassByCrit, false);
  ...
}

看完后自己是有個(gè)疑問的满葛,在添加到全局 hash 的時(shí)候,添加的是 ClassObject 對象罢屈,但是在搜索的時(shí)候確實(shí)以一個(gè)ClassMatchCriteria去尋找的嘀韧。

struct ClassMatchCriteria {
  const char* descriptor;
  Object* loader;
}

再來看 dvmHashTableLookup()是如何判斷搜索目標(biāo)一致性的,中間有一行

//Hash.cpp
(*cmpFunc)(pEntry->data, item) == 0

而 cmpFunc 是一個(gè)函數(shù)指針缠捌,而我們知道 pEntry->data 就是 ClassObject 對象锄贷,而 item 就是 ClassMatchCriteria 對象,現(xiàn)在來看 cmpFunc 這個(gè)函數(shù)指針曼月。

//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;
}

于是從源碼可以看到肃叶,一個(gè)類在 dvm 中裝載,是由其類的簽名和加載類的類加載器決定的十嘿。如果有一個(gè)不一致因惭,則認(rèn)識是兩個(gè)不同的ClassObject。


雙親委派模型規(guī)定了加載一個(gè)類的順序绩衷,所謂的加載器對于 DVM 來說地位都是一樣的蹦魔,可以認(rèn)為是與 類的descriptor共同作為 key 來標(biāo)志一個(gè) ClassObject

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末激率,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子勿决,更是在濱河造成了極大的恐慌乒躺,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件低缩,死亡現(xiàn)場離奇詭異嘉冒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)咆繁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門讳推,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人玩般,你說我怎么就攤上這事银觅。” “怎么了坏为?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵究驴,是天一觀的道長。 經(jīng)常有香客問我匀伏,道長洒忧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任够颠,我火速辦了婚禮熙侍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摧找。我一直安慰自己,他們只是感情好牢硅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布蹬耘。 她就那樣靜靜地躺著,像睡著了一般减余。 火紅的嫁衣襯著肌膚如雪综苔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天位岔,我揣著相機(jī)與錄音如筛,去河邊找鬼。 笑死抒抬,一個(gè)胖子當(dāng)著我的面吹牛杨刨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播擦剑,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妖胀,長吁一口氣:“原來是場噩夢啊……” “哼芥颈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赚抡,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤爬坑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后涂臣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盾计,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年赁遗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了署辉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吼和,死狀恐怖涨薪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炫乓,我是刑警寧澤刚夺,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站末捣,受9級特大地震影響侠姑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜箩做,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一莽红、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧邦邦,春花似錦安吁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至黔龟,卻和暖如春妇智,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氏身。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工巍棱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛋欣。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓航徙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親陷虎。 傳聞我的和親對象是個(gè)殘疾皇子捉偏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容