类文件解析003-解析常量池
本文我們來介紹ClassFileParser 解析常量池的過程.解析常量池的過程是在ClassFileParser::parseClassFile 通過parse_constant_pool 來實現的.
在parse_constant_pool 中的步驟如下:
獲取長度
在解析常量池之前,已經處理了魔數,讀取class文件主,次版本號及驗證.在ClassFileParser::parse_constant_pool中獲得常量池的長度的代碼如下:
ClassFileStream* cfs = stream(); constantPoolHandle nullHandle;cfs->guarantee_more(3, CHECK_(nullHandle)); // 獲得constant_pool_count 和第一個常量池表項的類型 // 1. 獲得長度 u2 length = cfs->get_u2_fast(); guarantee_property( length >= 1, "Illegal constant pool size %u in class file %s", length, CHECK_(nullHandle)); // 常量池的表項個數必須是大于等于1的此處回顧一下class文件的格式:
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; --> 在調用ClassFileParser::parse_constant_pool時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]; }其中對于常量池相關項的含義描述如下:
-
constant_pool_count
常量池計數器,constant_pool_count的值等于constant_pool表中的成員數加1。constant_pool表的索引值只有在大于0且小于constant_pool_count時才會被認為是有效的,對于long和double類型有例外情況。雖然值為0的constant_pool索引是無效的,但其他用到常量池的數據結構可以使用索引0來表示“不引用任何一個常量池項”的意思 該規范說明了constant_pool_count 必須是大于等于1
-
constant_pool[]
常量池,constant_pool是一種表結構,它包含Class文件結構及其子結構中引用的所有字符串常量、類或接口名、字段名和其它常量。常量池中的每一項都具備相同的格式特征——第一個字節作為類型標記用于識別該項是哪種類型的常量,稱為“tag byte”。常量池的索引范圍是1至constant_pool_count?1
其中常量池項都具有如下通用格式:
cp_info { u1 tag; u1 info[]; }常量池中,每個cp_info項的格式必須相同,它們都以一個表示cp_info類型的單字節“tag”項開頭。后面info[]項的內容tag由的類型所決定。tag有效的類型和對應的取值在表4.3列出。每個tag項必須跟隨2個或更多的字節,這些字節用于給定這個常量的信息,附加字節的信息格式由tag的值決定。jvm中可識別的tag如下:
因此,此處獲取常量池的長度就可以直接通過讀取2個字節后得出(當前得通過字節次序變換).
創建constantPoolOop
創建constantPoolOop調用的方法為: oopFactory::new_constantPool.代碼如下:
// 位于hotspot/src/share/vm/classfile/classFileParser.cppconstantPoolOop constant_pool =oopFactory::new_constantPool(length,methodOopDesc::IsSafeConc, // 默認為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)方法中創建的_constantPoolKlassObj.
調用constantPoolKlass::allocate 進行分配.其步驟如下:
調用constantPoolOopDesc::object_size(length) 計算要分配內存的大小
調用CollectedHeap::permanent_obj_allocate進行分配.
初始化constantPoolOop 實例域變量.這里涉及的代碼如下:
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 數組(常量池表項所使用的數組).
其中,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 后對齊的結果.
sizeof(constantPoolOopDesc) 的結果為 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; // 該字段不參與計算因此,在32位的情況下, oopDesc的大小為8.
而在64位的情況下,其大小為如果未開啟壓縮,則為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個,其大小為32.
因此,在32位上,sizeof(constantPoolOopDesc)/HeapWordSize + length = 40/4+ length = 10+ length.
其中,調用CollectedHeap::permanent_obj_allocat這一步驟,在類加載流程-002 中有介紹,這里就不在介紹了.不過這里需要補充一點的是,這里為什么分配大小時傳入的size為10+ length? 會不會不夠用呢?
答案在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");/** 注意,這里先執行HeapWord* obj = top();再執行 HeapWord* new_top = obj + size;,而最終返回的是obj指針,該指針指向的是原來的堆的最頂端,* 這樣,調用方通過指針可以還原從原頂端到當前堆頂之間的內存空間,將其強制轉換為常量池對象.*/HeapWord* obj = top();if (pointer_delta(end(), obj) >= size) {HeapWord* new_top = obj + size; // 將PermSpace內存區域的top指針往高地址移動了size大小的字節數,完成了當前被加載的類所對應的常量池的內存分配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;} }其中,方法的參數size 是一路傳過來的,分配時是通過HeapWord* new_top = obj + size; 進行分配的.這里涉及到了指針運算,因此,其最終的大小為: (10+ length) * 4 . (HeapWord 的大小為4).
接下來,我們介紹一下創建tag 數組(常量池表項所使用的數組)的過程,其代碼如下:
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());步驟如下:
首先,調用oopFactory::new_permanent_byteArray 創建typeArrayOopDesc::object_size(layout_helper(), length)大小的數組.
然后將其進行初始化,其值為:JVM_CONSTANT_Invalid
讓constantPoolOop 和tag 數組 進行關聯.
此時, constantPoolOop的內存結構如下:
這里有個問題,為啥constantPoolOop 為額外分配length個大小的內存,另外又用過tags指向一個typeArrayOop呢? 這個我們在下篇文章中通過介紹ClassFileParser::parse_constant_pool_entries來進行介紹.
總結
以上是生活随笔為你收集整理的类文件解析003-解析常量池的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一文读懂法拉第未来赴美上市:合并PSAC
- 下一篇: 程序员的45个习惯