接着上一篇继续分析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 constant pool entry and thus the same constant pool cache entry. All invoke
bytecodes but invokevirtual use only _f1 and the corresponding b1 bytecode, 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源码剖析系列文章!