常量池解析(2)


在parse_constant_pool()方法中調用parse_constant_pool_entries()方法對常量池中的各個項進行解析,方法的實現如下:

void ClassFileParser::parse_constant_pool_entries(int length, TRAPS) {
  // Use a local copy of ClassFileStream. It helps the C++ compiler to optimize
  // this function (_current can be allocated in a register, with scalar
  // replacement of aggregates). The _current pointer is copied back to
  // stream() when this function returns. DON'T call another method within
  // this method that uses stream().
  ClassFileStream*  cfs0 = stream();
  ClassFileStream   cfs1 = *cfs0;
  ClassFileStream*  cfs  = &cfs1;

  Handle class_loader(THREAD, _loader_data->class_loader());

  // Used for batching symbol allocations.
  const char*   names[SymbolTable::symbol_alloc_batch_size];
  int           lengths[SymbolTable::symbol_alloc_batch_size];
  int           indices[SymbolTable::symbol_alloc_batch_size];
  unsigned int  hashValues[SymbolTable::symbol_alloc_batch_size];
  int           names_count = 0;

  // parsing  Index 0 is unused
  for (int index = 1; index < length; index++) {
    // Each of the following case guarantees one more byte in the stream
    // for the following tag or the access_flags following constant pool,
    // so we don't need bounds-check for reading tag.
    u1 tag = cfs->get_u1_fast();
    switch (tag) {
      case JVM_CONSTANT_Class :
        {
          cfs->guarantee_more(3, CHECK);  // name_index, tag/access_flags
          u2 name_index = cfs->get_u2_fast();
          _cp->klass_index_at_put(index, name_index);
        }
        break;
      case JVM_CONSTANT_Fieldref :
        {
          cfs->guarantee_more(5, CHECK);  // class_index, name_and_type_index, tag/access_flags
          u2 class_index = cfs->get_u2_fast();
          u2 name_and_type_index = cfs->get_u2_fast();
          _cp->field_at_put(index, class_index, name_and_type_index);
        }
        break;
      case JVM_CONSTANT_Methodref :
        {
          cfs->guarantee_more(5, CHECK);  // class_index, name_and_type_index, tag/access_flags
          u2 class_index = cfs->get_u2_fast();
          u2 name_and_type_index = cfs->get_u2_fast();
          _cp->method_at_put(index, class_index, name_and_type_index);
        }
        break;
      case JVM_CONSTANT_InterfaceMethodref :
        {
          cfs->guarantee_more(5, CHECK);  // class_index, name_and_type_index, tag/access_flags
          u2 class_index = cfs->get_u2_fast();
          u2 name_and_type_index = cfs->get_u2_fast();
          _cp->interface_method_at_put(index, class_index, name_and_type_index);
        }
        break;
      case JVM_CONSTANT_String :
        {
          cfs->guarantee_more(3, CHECK);  // string_index, tag/access_flags
          u2 string_index = cfs->get_u2_fast();
          _cp->string_index_at_put(index, string_index);
        }
        break;
      case JVM_CONSTANT_MethodHandle :
      case JVM_CONSTANT_MethodType :
        if (tag == JVM_CONSTANT_MethodHandle) {
          cfs->guarantee_more(4, CHECK);  // ref_kind, method_index, tag/access_flags
          u1 ref_kind = cfs->get_u1_fast();
          u2 method_index = cfs->get_u2_fast();
          _cp->method_handle_index_at_put(index, ref_kind, method_index);
        } else if (tag == JVM_CONSTANT_MethodType) {
          cfs->guarantee_more(3, CHECK);  // signature_index, tag/access_flags
          u2 signature_index = cfs->get_u2_fast();
          _cp->method_type_index_at_put(index, signature_index);
        } else {
          ShouldNotReachHere();
        }
        break;
      case JVM_CONSTANT_InvokeDynamic :
        {
          cfs->guarantee_more(5, CHECK);  // bsm_index, nt, tag/access_flags
          u2 bootstrap_specifier_index = cfs->get_u2_fast();
          u2 name_and_type_index = cfs->get_u2_fast();
          if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index)
            _max_bootstrap_specifier_index = (int) bootstrap_specifier_index;  // collect for later
          _cp->invoke_dynamic_at_put(index, bootstrap_specifier_index, name_and_type_index);
        }
        break;
      case JVM_CONSTANT_Integer :
        {
          cfs->guarantee_more(5, CHECK);  // bytes, tag/access_flags
          u4 bytes = cfs->get_u4_fast();
          _cp->int_at_put(index, (jint) bytes);
        }
        break;
      case JVM_CONSTANT_Float :
        {
          cfs->guarantee_more(5, CHECK);  // bytes, tag/access_flags
          u4 bytes = cfs->get_u4_fast();
          _cp->float_at_put(index, *(jfloat*)&bytes);
        }
        break;
      case JVM_CONSTANT_Long :
        {
          cfs->guarantee_more(9, CHECK);  // bytes, tag/access_flags
          u8 bytes = cfs->get_u8_fast();
          _cp->long_at_put(index, bytes);
        }
        index++;   // Skip entry following eigth-byte constant, see JVM book p. 98
        break;
      case JVM_CONSTANT_Double :
        {
          cfs->guarantee_more(9, CHECK);  // bytes, tag/access_flags
          u8 bytes = cfs->get_u8_fast();
          _cp->double_at_put(index, *(jdouble*)&bytes);
        }
        index++;   // Skip entry following eigth-byte constant, see JVM book p. 98
        break;
      case JVM_CONSTANT_NameAndType :
        {
          cfs->guarantee_more(5, CHECK);  // name_index, signature_index, tag/access_flags
          u2 name_index = cfs->get_u2_fast();
          u2 signature_index = cfs->get_u2_fast();
          _cp->name_and_type_at_put(index, name_index, signature_index);
        }
        break;
      case JVM_CONSTANT_Utf8 :
        {
          cfs->guarantee_more(2, CHECK);  // utf8_length
          u2  utf8_length = cfs->get_u2_fast();
          u1* utf8_buffer = cfs->get_u1_buffer();
          assert(utf8_buffer != NULL, "null utf8 buffer");
          // Got utf8 string, guarantee utf8_length+1 bytes, set stream position forward.
          cfs->guarantee_more(utf8_length+1, CHECK);  // utf8 string, tag/access_flags
          cfs->skip_u1_fast(utf8_length);

          if (EnableInvokeDynamic && has_cp_patch_at(index)) {
            Handle patch = clear_cp_patch_at(index);

            char* str = java_lang_String::as_utf8_string(patch());
            // (could use java_lang_String::as_symbol instead, but might as well batch them)
            utf8_buffer = (u1*) str;
            utf8_length = (int) strlen(str);
          }

          unsigned int hash;
          Symbol* result = SymbolTable::lookup_only((char*)utf8_buffer, utf8_length, hash);
          if (result == NULL) {
            names[names_count] = (char*)utf8_buffer;
            lengths[names_count] = utf8_length;
            indices[names_count] = index;
            hashValues[names_count++] = hash;
            if (names_count == SymbolTable::symbol_alloc_batch_size) {
              SymbolTable::new_symbols(_loader_data, _cp, names_count, names, lengths, indices, hashValues, CHECK);
              names_count = 0;
            }
          } else {
            _cp->symbol_at_put(index, result);
          }
        }
        break;
      default:
        classfile_parse_error("Unknown constant tag %u in class file %s", tag, CHECK);
        break;
    }
  }

  // Allocate the remaining symbols
  if (names_count > 0) {
    SymbolTable::new_symbols(_loader_data, _cp, names_count, names, lengths, indices, hashValues, CHECK);
  }

  cfs0->set_current(cfs1.current());
}

循環處理length個常量池項,不過第一個常量池項不需要處理,所以循環下標index的值初始化為1。

如果要了解各個常量池項的具體結構,代碼的邏輯理解起來其實並不難。所有項的第一個字節都是用來描述常量池元素類型,調用cfs->get_u1_fast()獲取元素類型后,就可以通過switch語句分情況進行處理。

1、JVM_CONSTANT_Class項的解析

JVM_CONSTANT_Class格式如下:

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

調用cfs->get_u2_fast()方法獲取name_index,然后調用_cp->klass_index_at_put()方法進行存儲。_cp的類型為ConstantPool*,ConstantPool類中的klass_index_at_put()方法的實現如下:

// For temporary use while constructing constant pool
void klass_index_at_put(int which, int name_index) {
    tag_at_put(which, JVM_CONSTANT_ClassIndex);
    *int_at_addr(which) = name_index;
}

void tag_at_put(int which, jbyte t) {
    tags()->at_put(which, t);
}

jint* int_at_addr(int which) const {
     assert(is_within_bounds(which), "index out of bounds");
     return (jint*) &base()[which];
}
intptr_t* base() const {
	  return (intptr_t*) (
			        (  (char*) this  ) + sizeof(ConstantPool)
			  );
}

常量池項的下標與數組的下標是相同的,也就是說,如果當前JVM_CONSTANT_Class存儲在常量池中的下標為1處,則也要存儲到tags數組中下標為1的地方。同時要將名稱索引name_index保存到ConstantPool中存儲數據區的對應位置上。

舉個例子如下:

#1 = Class         #5        // TestClass
...
#5 = Utf8          TestClass 

假設JVM_CONSTANT_Class是常量池第一項,則解析完這一頂后的ConstantPool對象如下圖所示。

 

其中#0(表示常量池索引0)的值為0是因為在分配內存時會將其內存清零。 

2、CONSTANT_Fieldref_info項的解析

格式如下:

CONSTANT_Fieldref_info {
	u1 tag;
	u2 class_index;
	u2 name_and_type_index;
}

調用field_at_put()存儲class_index與name_and_type_index,方法的實現如下:

void field_at_put(int which, int class_index, int name_and_type_index) {
    tag_at_put(which, JVM_CONSTANT_Fieldref);
    *int_at_addr(which) = ((jint) name_and_type_index<<16) | class_index;
}

name_and_type_index存儲在高16位,class_index存儲在低16位。  

3、JVM_CONSTANT_Methodref項的解析

JVM_CONSTANT_Methodref項的格式如下:

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

按照格式讀取Class文件,獲取到相關屬性值后調用ConstantPool的method_at_put()方法進行存儲,這個方法的實現如下:

void method_at_put(int which, int class_index, int name_and_type_index) {
    tag_at_put(which, JVM_CONSTANT_Methodref);
    *int_at_addr(which) = ((jint) name_and_type_index<<16) | class_index;
}

由於ConstantPool數據區一個槽是一個指針類型的寬度,所以至少有32個位,又由於class_index與name_and_type_index屬性的類型為u2,這時候就可以使用高16位存儲name_and_type_index,低16位存儲class_index即可。  

4、JVM_CONSTANT_InterfaceMethodref項的解析

格式如下:

CONSTANT_InterfaceMethodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

調用的interface_method_at_put()方法的實現如下:

void interface_method_at_put(int which, int class_index, int name_and_type_index) {
    tag_at_put(which, JVM_CONSTANT_InterfaceMethodref);
    *int_at_addr(which) = ((jint) name_and_type_index<<16) | class_index;  // Not so nice
}

5、JVM_CONSTANT_String項的解析  

格式如下:

CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}

調用的string_index_at_put()方法的實現如下:

void string_index_at_put(int which, int string_index) {
    tag_at_put(which, JVM_CONSTANT_StringIndex);
    *int_at_addr(which) = string_index;
}

6、JVM_CONSTANT_MethodHandle項的解析  

格式如下:

CONSTANT_MethodHandle_info {
    u1 tag;
    u1 reference_kind;
    u2 reference_index;
}

調用的method_handle_index_at_put()方法的實現如下:

void method_handle_index_at_put(int which, int ref_kind, int ref_index) {
    tag_at_put(which, JVM_CONSTANT_MethodHandle);
    *int_at_addr(which) = ((jint) ref_index<<16) | ref_kind;
}

7、JVM_CONSTANT_MethodType項的解析  

格式如下:

CONSTANT_MethodType_info {
    u1 tag;
    u2 descriptor_index;
}

調用的method_type_index_at_put()方法的實現如下:

void method_type_index_at_put(int which, int ref_index) {
    tag_at_put(which, JVM_CONSTANT_MethodType);
    *int_at_addr(which) = ref_index;
}

8、JVM_CONSTANT_InvokeDynamic項的解析  

格式如下:

CONSTANT_InvokeDynamic_info {
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}

調用的invoke_dynamic_at_put()方法的實現如下:  

void invoke_dynamic_at_put(int which, int bootstrap_specifier_index, int name_and_type_index) {
    tag_at_put(which, JVM_CONSTANT_InvokeDynamic);
    *int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_specifier_index;
}

9、JVM_CONSTANT_Integer、JVM_CONSTANT_Float項的解析  

格式如下:

CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}

CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}

調用的方法分別為int_at_put()和float_at_put()方法,實現如下:

void int_at_put(int which, jint i) {
    tag_at_put(which, JVM_CONSTANT_Integer);
    *int_at_addr(which) = i;
}

void float_at_put(int which, jfloat f) {
    tag_at_put(which, JVM_CONSTANT_Float);
    *float_at_addr(which) = f;
}

10、JVM_CONSTANT_Long、JVM_CONSTANT_Double項的解析  

格式如下: 

CONSTANT_Long_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}

CONSTANT_Double_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}

調用的long_at_put()和double_at_put()方法的實現如下:

void long_at_put(int which, jlong l) {
      tag_at_put(which, JVM_CONSTANT_Long);
      // *long_at_addr(which) = l;
      Bytes::put_native_u8((address)long_at_addr(which), *(  (u8*) &l  ));
}

void double_at_put(int which, jdouble d) {
    tag_at_put(which, JVM_CONSTANT_Double);
    // *double_at_addr(which) = d;
    // u8 temp = *(u8*) &d;
    Bytes::put_native_u8((address) double_at_addr(which), *((u8*) &d));
}

調用的Bytes::put_native_u8()方法的實現如下:

static inline void put_native_u8(address p, u8 x)   { *(u8*)p = x; }

11、JVM_CONSTANT_NameAndType項的解析  

格式如下:

CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

調用的name_and_type_at_put()方法的實現如下:

void name_and_type_at_put(int which, int name_index, int signature_index) {
    tag_at_put(which, JVM_CONSTANT_NameAndType);
    *int_at_addr(which) = ((jint) signature_index<<16) | name_index;  // Not so nice
}

12、JVM_CONSTANT_Utf8項的解析    

格式如下:

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

在HotSpot虛擬機中,字符串通常都會表示為Symbol對象,這樣有利於使用符號表來存儲字符串,對於2個相同的字符串來說,完全可以使用同一個Symbol對象來表示。這樣就可以在ConstantPool數據區相應槽位上存儲指向Symbol的指針即可。

調用SymbolTable::lookup_only()方法從符號表中查找對應的Symbol對象,如果查找不到需要暫時將相關的信息存儲到臨時的names、lengths、indices與hashValues數組中,這樣就可以調用SymbolTable::new_symbols()進行批量添加Symbol對象來提高效率;如果找到對應的Symbol對象,則調用symbol_at_put()方法,實現如下:

void symbol_at_put(int which, Symbol* s) {
    assert(s->refcount() != 0, "should have nonzero refcount");
    tag_at_put(which, JVM_CONSTANT_Utf8);
    *symbol_at_addr(which) = s;
}

Symbol** symbol_at_addr(int which) const {
    assert(is_within_bounds(which), "index out of bounds");
    return (Symbol**) &base()[which];
}

將指向Symbol對象的指針存儲到指定的位置。 

如果Symbol對象表示的是類名稱,那么后面是類連接后,相應索引位置上的值會更新為指向InstanceKlass實例的指針,后面會詳細介紹。

相關文章的鏈接如下:

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) 

作者持續維護的個人博客classloading.com

關注公眾號,有HotSpot源碼剖析系列文章!

      

參考:

(1)https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4

(2)《Java虛擬機原理圖解》 1.2.2、Class文件中的常量池詳解(上) 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM