类文件解析003-解析常量池
本文我們來(lái)介紹ClassFileParser 解析常量池的過(guò)程.解析常量池的過(guò)程是在ClassFileParser::parseClassFile 通過(guò)parse_constant_pool 來(lái)實(shí)現(xiàn)的.
在parse_constant_pool 中的步驟如下:
獲取長(zhǎng)度
在解析常量池之前,已經(jīng)處理了魔數(shù),讀取class文件主,次版本號(hào)及驗(yàn)證.在ClassFileParser::parse_constant_pool中獲得常量池的長(zhǎng)度的代碼如下:
ClassFileStream* cfs = stream(); constantPoolHandle nullHandle;cfs->guarantee_more(3, CHECK_(nullHandle)); // 獲得constant_pool_count 和第一個(gè)常量池表項(xiàng)的類(lèi)型 // 1. 獲得長(zhǎng)度 u2 length = cfs->get_u2_fast(); guarantee_property( length >= 1, "Illegal constant pool size %u in class file %s", length, CHECK_(nullHandle)); // 常量池的表項(xiàng)個(gè)數(shù)必須是大于等于1的此處回顧一下class文件的格式:
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; --> 在調(diào)用ClassFileParser::parse_constant_pool時(shí)ClassFileStream的指針就指向這里 cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; }其中對(duì)于常量池相關(guān)項(xiàng)的含義描述如下:
-
constant_pool_count
常量池計(jì)數(shù)器,constant_pool_count的值等于constant_pool表中的成員數(shù)加1。constant_pool表的索引值只有在大于0且小于constant_pool_count時(shí)才會(huì)被認(rèn)為是有效的,對(duì)于long和double類(lèi)型有例外情況。雖然值為0的constant_pool索引是無(wú)效的,但其他用到常量池的數(shù)據(jù)結(jié)構(gòu)可以使用索引0來(lái)表示“不引用任何一個(gè)常量池項(xiàng)”的意思 該規(guī)范說(shuō)明了constant_pool_count 必須是大于等于1
-
constant_pool[]
常量池,constant_pool是一種表結(jié)構(gòu),它包含Class文件結(jié)構(gòu)及其子結(jié)構(gòu)中引用的所有字符串常量、類(lèi)或接口名、字段名和其它常量。常量池中的每一項(xiàng)都具備相同的格式特征——第一個(gè)字節(jié)作為類(lèi)型標(biāo)記用于識(shí)別該項(xiàng)是哪種類(lèi)型的常量,稱(chēng)為“tag byte”。常量池的索引范圍是1至constant_pool_count?1
其中常量池項(xiàng)都具有如下通用格式:
cp_info { u1 tag; u1 info[]; }常量池中,每個(gè)cp_info項(xiàng)的格式必須相同,它們都以一個(gè)表示cp_info類(lèi)型的單字節(jié)“tag”項(xiàng)開(kāi)頭。后面info[]項(xiàng)的內(nèi)容tag由的類(lèi)型所決定。tag有效的類(lèi)型和對(duì)應(yīng)的取值在表4.3列出。每個(gè)tag項(xiàng)必須跟隨2個(gè)或更多的字節(jié),這些字節(jié)用于給定這個(gè)常量的信息,附加字節(jié)的信息格式由tag的值決定。jvm中可識(shí)別的tag如下:
因此,此處獲取常量池的長(zhǎng)度就可以直接通過(guò)讀取2個(gè)字節(jié)后得出(當(dāng)前得通過(guò)字節(jié)次序變換).
創(chuàng)建constantPoolOop
創(chuàng)建constantPoolOop調(diào)用的方法為: oopFactory::new_constantPool.代碼如下:
// 位于hotspot/src/share/vm/classfile/classFileParser.cppconstantPoolOop constant_pool =oopFactory::new_constantPool(length,methodOopDesc::IsSafeConc, // 默認(rèn)為trueCHECK_(nullHandle));// 位于hotspot/src/share/vm/memory/oopFactory.cpp constantPoolOop oopFactory::new_constantPool(int length,bool is_conc_safe,TRAPS) {constantPoolKlass* ck = constantPoolKlass::cast(Universe::constantPoolKlassObj());return ck->allocate(length, is_conc_safe, CHECK_NULL); }獲取在Universe::genesis(TRAPS)方法中創(chuàng)建的_constantPoolKlassObj.
調(diào)用constantPoolKlass::allocate 進(jìn)行分配.其步驟如下:
調(diào)用constantPoolOopDesc::object_size(length) 計(jì)算要分配內(nèi)存的大小
調(diào)用CollectedHeap::permanent_obj_allocate進(jìn)行分配.
初始化constantPoolOop 實(shí)例域變量.這里涉及的代碼如下:
c->set_length(length);c->set_tags(NULL);c->set_cache(NULL);c->set_operands(NULL);c->set_pool_holder(NULL);c->set_flags(0);// only set to non-zero if constant pool is merged by RedefineClassesc->set_orig_length(0);// if constant pool may change during RedefineClasses, it is created// unsafe for GC concurrent processing.c->set_is_conc_safe(is_conc_safe);初始化tag 數(shù)組(常量池表項(xiàng)所使用的數(shù)組).
其中,constantPoolOopDesc::object_size(length) 的方法如下:
static int header_size() { return sizeof(constantPoolOopDesc)/HeapWordSize; } // 10 static int object_size(int length) { return align_object_size(header_size() + length); }inline intptr_t align_object_size(intptr_t size) {return align_size_up(size, MinObjAlignment); // 其中 MinObjAlignment 為2 }#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1))inline intptr_t align_size_up(intptr_t size, intptr_t alignment) {return align_size_up_(size, alignment); }總之,其最終返回的大小為: sizeof(constantPoolOopDesc)/HeapWordSize + length 后對(duì)齊的結(jié)果.
sizeof(constantPoolOopDesc) 的結(jié)果為 40,原因如下:
constantPoolOopDesc 繼承自oopDesc,其具有如下字段:
volatile markOop _mark;union _metadata {wideKlassOop _klass;narrowOop _compressed_klass;} _metadata;// Fast access to barrier set. Must be initialized.static BarrierSet* _bs; // 該字段不參與計(jì)算因此,在32位的情況下, oopDesc的大小為8.
而在64位的情況下,其大小為如果未開(kāi)啟壓縮,則為16,否則為12(_metadata 的大小為4).
而在constantPoolOopDesc 中如下字段:
typeArrayOop _tags; // the tag array describing the constant pool's contents constantPoolCacheOop _cache; // the cache holding interpreter runtime information klassOop _pool_holder; // the corresponding class typeArrayOop _operands; // for variable-sized (InvokeDynamic) nodes, usually empty int _flags; // a few header bits to describe contents for GC int _length; // number of elements in the array volatile bool _is_conc_safe; // if true, safe for concurrent// GC processing // only set to non-zero if constant pool is merged by RedefineClasses int _orig_length;共8個(gè),其大小為32.
因此,在32位上,sizeof(constantPoolOopDesc)/HeapWordSize + length = 40/4+ length = 10+ length.
其中,調(diào)用CollectedHeap::permanent_obj_allocat這一步驟,在類(lèi)加載流程-002 中有介紹,這里就不在介紹了.不過(guò)這里需要補(bǔ)充一點(diǎn)的是,這里為什么分配大小時(shí)傳入的size為10+ length? 會(huì)不會(huì)不夠用呢?
答案在MutableSpace::allocate中,代碼如下:
HeapWord* MutableSpace::allocate(size_t size) {assert(Heap_lock->owned_by_self() ||(SafepointSynchronize::is_at_safepoint() &&Thread::current()->is_VM_thread()),"not locked");/** 注意,這里先執(zhí)行HeapWord* obj = top();再執(zhí)行 HeapWord* new_top = obj + size;,而最終返回的是obj指針,該指針指向的是原來(lái)的堆的最頂端,* 這樣,調(diào)用方通過(guò)指針可以還原從原頂端到當(dāng)前堆頂之間的內(nèi)存空間,將其強(qiáng)制轉(zhuǎn)換為常量池對(duì)象.*/HeapWord* obj = top();if (pointer_delta(end(), obj) >= size) {HeapWord* new_top = obj + size; // 將PermSpace內(nèi)存區(qū)域的top指針往高地址移動(dòng)了size大小的字節(jié)數(shù),完成了當(dāng)前被加載的類(lèi)所對(duì)應(yīng)的常量池的內(nèi)存分配set_top(new_top);assert(is_object_aligned((intptr_t)obj) && is_object_aligned((intptr_t)new_top),"checking alignment");return obj;} else {return NULL;} }其中,方法的參數(shù)size 是一路傳過(guò)來(lái)的,分配時(shí)是通過(guò)HeapWord* new_top = obj + size; 進(jìn)行分配的.這里涉及到了指針運(yùn)算,因此,其最終的大小為: (10+ length) * 4 . (HeapWord 的大小為4).
接下來(lái),我們介紹一下創(chuàng)建tag 數(shù)組(常量池表項(xiàng)所使用的數(shù)組)的過(guò)程,其代碼如下:
constantPoolHandle pool (THREAD, c); typeArrayOop t_oop = oopFactory::new_permanent_byteArray(length, CHECK_NULL); typeArrayHandle tags (THREAD, t_oop); for (int index = 0; index < length; index++) { tags()->byte_at_put(index, JVM_CONSTANT_Invalid); } pool->set_tags(tags());步驟如下:
首先,調(diào)用oopFactory::new_permanent_byteArray 創(chuàng)建typeArrayOopDesc::object_size(layout_helper(), length)大小的數(shù)組.
然后將其進(jìn)行初始化,其值為:JVM_CONSTANT_Invalid
讓constantPoolOop 和tag 數(shù)組 進(jìn)行關(guān)聯(lián).
此時(shí), constantPoolOop的內(nèi)存結(jié)構(gòu)如下:
這里有個(gè)問(wèn)題,為啥constantPoolOop 為額外分配length個(gè)大小的內(nèi)存,另外又用過(guò)tags指向一個(gè)typeArrayOop呢? 這個(gè)我們?cè)谙缕恼轮型ㄟ^(guò)介紹ClassFileParser::parse_constant_pool_entries來(lái)進(jìn)行介紹.
總結(jié)
以上是生活随笔為你收集整理的类文件解析003-解析常量池的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一文读懂法拉第未来赴美上市:合并PSAC
- 下一篇: 程序员的45个习惯