類的連線之重寫(2)
接著上一篇繼續分析Rewriter::Rewriter()建構函式中完成的邏輯。在建構函式中會呼叫make_constant_pool_cache()函式,不過在先介紹這個函式之前,需要介紹一下ConstantPoolCache與ConstantPoolCacheEntry。這兩個類都定義在cpCache.hpp檔案中。
1、ConstantPoolCache類
ConstantPoolCache類儲存了連線過程中的一些資訊,從而讓程式在解釋執行的過程中避免重複執行連線的過程。這個類的定義如下:
// A constant pool cache is a runtime data structure set aside to a constant pool. The cache // holds interpreter runtime information for all field access and invoke bytecodes. The cache // is created and initialized before a class is actively used (i.e., initialized), the individual // cache entries are filled at resolution (i.e., "link") time (see also: rewriter.*). class ConstantPoolCache: public MetaspaceObj { private: int _length; ConstantPool* _constant_pool; // the corresponding constant pool // Constructor ConstantPoolCache(int length, const intStack& inverse_index_map, const intStack& invokedynamic_inverse_index_map, const intStack& invokedynamic_references_map) : _length(length), _constant_pool(NULL) { initialize( inverse_index_map, invokedynamic_inverse_index_map, invokedynamic_references_map); } private: static int header_size() { return sizeof(ConstantPoolCache) / HeapWordSize; // 2個字,一個字包含有8位元組 } static int size(int length) { // 返回的是字數量 // ConstantPoolCache加上length個ConstantPoolCacheEntry的大小 // in_words(ConstantPoolCacheEntry::size())=4 return align_object_size(header_size() + length * in_words(ConstantPoolCacheEntry::size())); } public: int size() const { return size(length()); } private: ConstantPoolCacheEntry* base() const { // 這就說明在ConstantPoolCache之後緊接著的是ConstantPoolCacheEntry項 return (ConstantPoolCacheEntry*)( (address)this + in_bytes(base_offset()) ); } public: // Fetches the entry at the given index. // In either case the index must not be encoded or byte-swapped in any way. ConstantPoolCacheEntry* entry_at(int i) const { assert(0 <= i && i < length(), "index out of bounds"); return base() + i; } // Code generation static ByteSize base_offset() { return in_ByteSize(sizeof(ConstantPoolCache)); } static ByteSize entry_offset(int raw_index) { int index = raw_index; return (base_offset() + ConstantPoolCacheEntry::size_in_bytes() * index); } };
如上類刪除了一些實現簡單或不太重要的方法,保留了屬性及重要方法的定義。這個類中定義了2個屬性_length及_constant_pool,_length表示,而_constant_pool表示這是儲存的哪個常量池連線的資訊存,通常快取具體的資訊通過ConstantPoolCacheEntry來表示,它們在記憶體中的佈局就是一個ConstantPoolCache後緊跟著數個ConstantPoolCacheEntry。這樣size()及base()等方法的實現就不難簡單了。
ConstantPoolCache主要用於快取某些位元組碼指令所需的解析好的常量項,例如給[get|put]static、[get|put]field、invoke[static|special|virtual|interface|dynamic]等指令對應的常量池項使用。
2、ConstantPoolCacheEntry類
ConstantPoolCacheEntry類及重要屬性的定義如下:
class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { private: volatile intx _indices; // constant pool index & rewrite bytecodes volatile Metadata* _f1; // entry specific metadata field volatile intx _f2; // entry specific int/metadata field volatile intx _flags; // flags // ... }
這4個屬效能夠表示非常多的資訊。這4個欄位表示的資訊如下圖所示。
這4個欄位長度相同,以32為作業系統為例來介紹這4個欄位。如果當前的ConstantPoolCacheEntry表示的是欄位入口,則幾個欄位的資訊如下圖所示。
如果當前的ConstantPoolCacheEntry表示的是方法入口,則幾個欄位的資訊如下圖所示。
位元組碼呼叫方法的指令主要有如下幾個:
(1)invokevirtual,通過vtable進行方法分發
- _f1:沒有使用
- _f2:呼叫非final的virtual方法,_f2欄位中則存放目標方法在vtable中的索引編號。如果是virtual final方法,_f2欄位也直接指向目標方法的Method。
(2)invokeinterface,通過itable進行方法分發
- _f1:_f1欄位指向對應介面的Klass
- _f2:存放的則是方法位於itable表中的索引編號
(3)invokespecial,呼叫private和構造方法,不需要分發機制
- _f1:_f1欄位表示指向目標方法Method(用它可以定位Java方法在記憶體中的具體位置,從而實現方法呼叫)
- _f2:沒有使用
(4)invokestatic,呼叫靜態方法,不需要分發機制
- _f1:_f1欄位表示指向目標方法Method(用它可以定位Java方法在記憶體中的具體位置,從而實現方法呼叫)
- _f2:沒有使用
Note: invokevirtual & invokespecial bytecodes can share the same constantpool entry and thus the same constant pool cache entry. All invoke
bytecodes but invokevirtual use only _f1 and the corresponding b1bytecode, while invokevirtual uses only _f2 and the corresponding
b2 bytecode. The value of _flags is shared for both types of entries.
之前介紹了重寫時呼叫Rewriter::Rewriter()建構函式,在建構函式中還會呼叫Rewriter::make_constant_pool_cache()方法,這個方法的實現如下:
// Creates a constant pool cache given a CPC map void Rewriter::make_constant_pool_cache(TRAPS) { InstanceKlass* ik = _pool->pool_holder(); ClassLoaderData* loader_data = ik->class_loader_data(); ConstantPoolCache* cache = ConstantPoolCache::allocate(loader_data, _cp_cache_map, _invokedynamic_cp_cache_map, _invokedynamic_references_map, CHECK); // initialize object cache in constant pool _pool->initialize_resolved_references(loader_data, _resolved_references_map, _resolved_reference_limit, CHECK); _pool->set_cache(cache); // 設定ConstantPool類中的_cache屬性 cache->set_constant_pool(_pool()); // 設定ConstantPoolCache中的_constant_pool屬性 }
呼叫的ConstantPoolCache::allocate()函式的實現如下:
ConstantPoolCache* ConstantPoolCache::allocate( ClassLoaderData* loader_data, const intStack& index_map, const intStack& invokedynamic_index_map, const intStack& invokedynamic_map, TRAPS ){ const int length = index_map.length() + invokedynamic_index_map.length(); int size = ConstantPoolCache::size(length); return new (loader_data, size, false, MetaspaceObj::ConstantPoolCacheType, THREAD) ConstantPoolCache( length, index_map, invokedynamic_index_map, invokedynamic_map); }
如上方法中呼叫的ConstantPoolCache::size()函式的實現如下:
static int size(int length) { // 返回的是字數量 // ConstantPoolCache加上length個ConstantPoolCacheEntry的大小 // in_words(ConstantPoolCacheEntry::size()) 的值為4 return align_object_size(header_size() + length * in_words(ConstantPoolCacheEntry::size())); }
index_map和invokedynamic_index_map中儲存的是常量池索引,這些索引需要建立對應的新的資料結構以表達更多的資訊。
呼叫ConstantPoolCache類的建構函式,如下:
// Constructor ConstantPoolCache(int length, const intStack& inverse_index_map, const intStack& invokedynamic_inverse_index_map, const intStack& invokedynamic_references_map) : _length(length), _constant_pool(NULL) { initialize( inverse_index_map, invokedynamic_inverse_index_map, invokedynamic_references_map); } void ConstantPoolCache::initialize(const intArray& inverse_index_map, const intArray& invokedynamic_inverse_index_map, const intArray& invokedynamic_references_map) { for (int i = 0; i < inverse_index_map.length(); i++) { ConstantPoolCacheEntry* e = entry_at(i); int original_index = inverse_index_map[i]; e->initialize_entry(original_index); // 為ConstantPoolCacheEntry::_indices屬性賦值 assert(entry_at(i) == e, "sanity"); } // ... } void ConstantPoolCacheEntry::initialize_entry(int index) { assert(0 < index && index < 0x10000, "sanity check"); _indices = index; _f1 = NULL; _f2 = _flags = 0; assert(constant_pool_index() == index, ""); }
從inverse_index_map中取出原常量池索引後,儲存到_indices中,之前介紹過,_indices的低16位儲存原常量池索引,而傳遞的引數也一定不會超過16位所能表示的最大值。而對於_f1暫時初始化為NULL,_f2與_flags暫時初始化為0,後面還會看到對這些欄位的初始化過程。
Rewriter::make_constant_pool_cache()函式中呼叫的ConstantPool::initialize_resolved_references()函式的實現如下:
// Create resolved_references array and mapping array for original cp indexes // The ldc bytecode was rewritten to have the resolved reference array index so need a way // to map it back for resolving and some unlikely miscellaneous uses. // The objects created by invokedynamic are appended to this list. void ConstantPool::initialize_resolved_references(ClassLoaderData* loader_data, intStack reference_map, int constant_pool_map_length, TRAPS ){ // Initialized the resolved object cache. int map_length = reference_map.length(); if (map_length > 0) { // Only need mapping back to constant pool entries. The map isn't used for // invokedynamic resolved_reference entries. For invokedynamic entries, // the constant pool cache index has the mapping back to both the constant // pool and to the resolved reference index. if (constant_pool_map_length > 0) { Array<u2>* om = MetadataFactory::new_array<u2>(loader_data, constant_pool_map_length, CHECK); for (int i = 0; i < constant_pool_map_length; i++) { int x = reference_map.at(i); om->at_put(i, (jushort)x); } set_reference_map(om); } // Create Java array for holding resolved strings, methodHandles, // methodTypes, invokedynamic and invokehandle appendix objects, etc. objArrayOop stom = oopFactory::new_objArray(SystemDictionary::Object_klass(), map_length, CHECK); Handle refs_handle(THREAD, (oop)stom); // must handleize. jobject x = loader_data->add_handle(refs_handle); set_resolved_references(x); } }
為ConstantPool類中的如下屬性設定了值:
// Array of resolved objects from the constant pool and map from resolved // object index to original constant pool index jobject _resolved_references; // jobject是指標型別 Array<u2>* _reference_map;
對於引用來說,這2個屬性可完成從以連線的引用索引到原常量池索引的對映,後面會接觸到相關應用。這部分內容不太理解也沒關係,我們在後面介紹在invokevirtual、invokespecial等位元組碼指令時,再重新梳理一下邏輯後就明白了。
相關文章的連結如下:
1、在Ubuntu 16.04上編譯OpenJDK8的原始碼
13、類載入器
14、類的雙親委派機制
15、核心類的預裝載
16、Java主類的裝載
17、觸發類的裝載
18、類檔案介紹
19、檔案流
20、解析Class檔案
21、常量池解析(1)
22、常量池解析(2)
23、欄位解析(1)
24、欄位解析之偽共享(2)
25、欄位解析(3)
28、方法解析
29、klassVtable與klassItable類的介紹
30、計算vtable的大小
31、計算itable的大小
32、解析Class檔案之建立InstanceKlass物件
33、欄位解析之欄位注入
34、類的連線
35、類的連線之驗證
36、類的連線之重寫(1)
作者持續維護的個人部落格classloading.com。
關注公眾號,有HotSpot原始碼剖析系列文章!