类的连接之重写(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表示的是字段入口,则几个字段的信息如下图所示。

类的连接之重写(2) 

如果当前的ConstantPoolCacheEntry表示的是方法入口,则几个字段的信息如下图所示。

类的连接之重写(2)  

字节码调用方法的指令主要有如下几个:

(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的源代码 

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程 

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)  

7、HotSpot的类模型(3) 

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)  

10、HotSpot的对象模型(6) 

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器 

14、类的双亲委派机制 

15、核心类的预装载

16、Java主类的装载  

17、触发类的装载  

18、类文件介绍 

19、文件流 

20、解析Class文件 

21、常量池解析(1) 

22、常量池解析(2)

23、字段解析(1)

24、字段解析之伪共享(2) 

25、字段解析(3)  

26、字段解析之OopMapBlock(4)

27、方法解析之Method与ConstMethod介绍  

28、方法解析

29、klassVtable与klassItable类的介绍  

30、计算vtable的大小 

31、计算itable的大小 

32、解析Class文件之创建InstanceKlass对象 

33、字段解析之字段注入 

34、类的连接  

35、类的连接之验证 

36、类的连接之重写(1) 

作者持续维护的个人博客  classloading.com

关注公众号,有HotSpot源码剖析系列文章!

类的连接之重写(2) 

 

上一篇:【tensorflow】模块—基础—计算图、张量、session、constant、variable、placeholder、feed


下一篇:2存储过程-变量声明