解析Class文件之創建InstanceKlass對象


ClassFileParser::parseClassFile()方法會將解析Class文件的大部分結果保存到instanceKlass對象中。創建instanceKlass對象的代碼如下: 

int total_oop_map_size2 = InstanceKlass::nonstatic_oop_map_size(info.total_oop_map_count);

// ReferenceType是枚舉類,定義如下:
/*enum ReferenceType {
      REF_NONE,      // Regular class
      REF_OTHER,     // Subclass of java/lang/ref/Reference, but not subclass of one of the classes below
      REF_SOFT,      // Subclass of java/lang/ref/SoftReference
      REF_WEAK,      // Subclass of java/lang/ref/WeakReference
      REF_FINAL,     // Subclass of java/lang/ref/FinalReference
      REF_PHANTOM    // Subclass of java/lang/ref/PhantomReference
}; */
// Compute reference type
ReferenceType rt; // 與強引用、弱引用等有關
if (super_klass() == NULL) {
       rt = REF_NONE;
} else {
       rt = super_klass->reference_type();
}

// We can now create the basic Klass* for this klass
InstanceKlass*  skc = super_klass();
bool            isnotnull = !host_klass.is_null();
_klass = InstanceKlass::allocate_instance_klass(loader_data,
                                                    vtable_size,
                                                    itable_size,
                                                    info.static_field_size, // 注意 info.static_field_size 會被傳進去,用於分配空間。
                                                    total_oop_map_size2,
                                                    rt,
                                                    access_flags,
                                                    name,
                                                    skc,
						    isnotnull,
                                                    CHECK_(nullHandle));
instanceKlassHandle   this_klass(THREAD, _klass);

調用InstanceKlass::allocate_instance_klass()方法創建InstanceKlass對象,需要傳入itable與vtable的大小,另外還需要傳入static_field_size與OopMapBlock。這些都是在創建相關對象時計算內存占用大小所必須的參數。方法的實現如下:

InstanceKlass* InstanceKlass::allocate_instance_klass(
	ClassLoaderData* loader_data,
	int           vtable_len,
	int           itable_len,
	int           static_field_size,
	int           nonstatic_oop_map_size,
	ReferenceType rt,
	AccessFlags   access_flags,
	Symbol*       name,
	Klass*        super_klass,
	bool          is_anonymous,
	TRAPS
){
  bool  isinterf = access_flags.is_interface();
  int   size = InstanceKlass::size(vtable_len,
								 itable_len,
								 nonstatic_oop_map_size,
		                         isinterf,
								 is_anonymous);

  // Allocation
  InstanceKlass* ik;
  ///////////////////////////////////////////////////////////////////////
  if (rt == REF_NONE) {
    if (name == vmSymbols::java_lang_Class()) {
      ik = new (loader_data, size, THREAD) InstanceMirrorKlass(
                       vtable_len,
					   itable_len,
					   static_field_size,
					   nonstatic_oop_map_size,
					   rt,
                       access_flags,
					   is_anonymous);
    } else if (
    	  name == vmSymbols::java_lang_ClassLoader() ||
          (
             SystemDictionary::ClassLoader_klass_loaded() &&
             super_klass != NULL &&
			 // ClassLoader_klass為java_lang_ClassLoader
             super_klass->is_subtype_of(SystemDictionary::ClassLoader_klass())
		  )
    ){
      ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass(
                       vtable_len,
					   itable_len,
					   static_field_size,
					   nonstatic_oop_map_size,
					   rt,
                       access_flags,
					   is_anonymous);
    } else {
      // normal class
      ik = new (loader_data, size, THREAD) InstanceKlass(
				vtable_len, itable_len,
				static_field_size,
				nonstatic_oop_map_size,
				rt,
				access_flags,
				is_anonymous);
    }
  }
  ///////////////////////////////////////////////////////////////////////
  else {
    // reference klass
    ik = new (loader_data, size, THREAD) InstanceRefKlass(
				vtable_len, itable_len,
				static_field_size,
				nonstatic_oop_map_size,
				rt,
				access_flags,
				is_anonymous);
  }
  ///////////////////////////////////////////////////////////////////////

  // 添加所有類型到我們內部類加載器列表中,包括在根加載器中的類
  // Add all classes to our internal class loader list here,
  // including classes in the bootstrap (NULL) class loader.
  // loader_data的類型為ClassLoaderData*,通過ClassLoaderData中的_klasses保持通過InstanceKlass._next_link屬性保持的列表
  loader_data->add_class(ik);
  return ik;
}

這個方法之前在介紹InstanceKlass對象時詳細介紹過。

方法調用InstanceKlass::size()計算內存占用的大小,然后創建對應的C++對象來表示Java類型。

當rt等於REF_NONE時,也就是rt為非Reference類型時,會根據類名創建對應C++類的對象。Class類通過InstanceMirrorKlass對象表示、ClassLoader類或ClassLoader的子類通過InstanceClassLoaderKlass對象表示、普通類通過InstanceKlass對象表示。當rt不為REF_NONE時,也就是rt為Referece類型時,通過InstanceRefKlass對象來表示。這里只看InstanceKlass對象的創建過程,調用的構造函數如下:

InstanceKlass::InstanceKlass(
	 int vtable_len,
	 int itable_len,
	 int static_field_size, // 注意這個靜態變量大小的分配
	 int nonstatic_oop_map_size,
	 ReferenceType rt,
	 AccessFlags access_flags,
	 bool is_anonymous
) {
  No_Safepoint_Verifier no_safepoint; // until k becomes parsable
  bool tmp = access_flags.is_interface();
  int iksize = InstanceKlass::size(vtable_len,
								   itable_len,
							       nonstatic_oop_map_size,
                                   tmp,
								   is_anonymous);

  set_vtable_length(vtable_len);
  set_itable_length(itable_len);
  set_static_field_size(static_field_size);
  set_nonstatic_oop_map_size(nonstatic_oop_map_size);
  set_access_flags(access_flags);
  _misc_flags = 0;  // initialize to zero
  set_is_anonymous(is_anonymous);
  assert(size() == iksize, "wrong size for object");

  // ...
  set_init_state(InstanceKlass::allocated);   // 注意在這里設置了類的狀態為分配
  // ...

  // initialize the non-header words to zero
  intptr_t* p = (intptr_t*)this;
  for (int index = InstanceKlass::header_size(); index < iksize; index++) {
    p[index] = NULL_WORD;
  }

  // Set temporary value until parseClassFile updates it with the real instance size.
  jint tti = Klass::instance_layout_helper(0, true);
  set_layout_helper(tti);
}

可以看到在創建InstanceKlass時初始化了許多參數,也就是說解析Class文件的相關信息大多都會通過InstanceKlass等對象的屬性保存起來,以支持虛擬機后續的運行。在構造函數中還需會將除header外的字初始化為NULL_WORD,將此類代表的Java類所創建出來的Java對象的大小初始化為0,后續會在parseClassFile()方法中更新這個值。 

在創建instanceKlass實例時,通過向構造函數中傳遞一些參數來初始化相關參數,另外還會調用相關set方法來設置參數,重要的參數如下:

jint lh = Klass::instance_layout_helper(info.instance_size, false);
this_klass->set_layout_helper(lh);

// Not yet(還沒有,還沒): supers are done below to support the new subtype-checking fields
this_klass->set_class_loader_data(loader_data);
this_klass->set_nonstatic_field_size(info.nonstatic_field_size);
this_klass->set_has_nonstatic_fields(info.has_nonstatic_fields);
this_klass->set_static_oop_field_count(fac.count[STATIC_OOP]);
// 有對_local_interfaces與_transitive_interfaces等屬性的設置邏輯
apply_parsed_class_metadata(this_klass, java_fields_count, CHECK_NULL);

if (has_final_method) {
  this_klass->set_has_final_method();
}
this_klass->copy_method_ordering(method_ordering, CHECK_NULL);
// The InstanceKlass::_methods_jmethod_ids cache
// is managed on the assumption that the initial cache
// size is equal to the number of methods in the class. If
// that changes, then InstanceKlass::idnum_can_increment()
// has to be changed accordingly.
this_klass->set_initial_method_idnum(methods->length());
this_klass->set_name(cp->klass_name_at(this_class_index));
if (is_anonymous()){  // I am well known to myself
  cp->klass_at_put(this_class_index, this_klass()); // eagerly resolve
}
this_klass->set_minor_version(minor_version);
this_klass->set_major_version(major_version);
this_klass->set_has_default_methods(has_default_methods);

// Set up Method*::intrinsic_id as soon as(一...就...) we know the names of methods.
// (We used to do this lazily, but now we query it in Rewriter,
// which is eagerly done for every method, so we might as well(也;同樣) do it now,
// when everything is fresh in memory.)
if (Method::klass_id_for_intrinsics(this_klass()) != vmSymbols::NO_SID) {
  for (int j = 0; j < methods->length(); j++) {
	 Method* md = methods->at(j);
	 md->init_intrinsic_id();
  }
}

// ...

// Miranda methods
if ( (num_miranda_methods > 0) ||
	 // if this class introduced new miranda methods or
	 (
		super_klass.not_null() &&
		(super_klass->has_miranda_methods())
	 )
	 // super class exists and this class inherited miranda methods
){
   this_klass->set_has_miranda_methods(); // then set a flag
}

// Fill in information needed to compute superclasses.
Klass* sk = super_klass();
this_klass->initialize_supers(sk, CHECK_(nullHandle));

// Initialize itable offset tables
klassItable::setup_itable_offset_table(this_klass);

// Compute transitive closure(閉包) of interfaces this class implements
// Do final class setup
fill_oop_maps(this_klass,
		info.nonstatic_oop_map_count,
		info.nonstatic_oop_offsets,
		info.nonstatic_oop_counts);

// Fill in  has_finalizer/has_vanilla_constructor/layout_helper
set_precomputed_flags(this_klass);


// Allocate mirror and initialize static fields
java_lang_Class::create_mirror(this_klass, protection_domain, CHECK_(nullHandle));

這里我們看到了許多之前介紹過的點,比如initialize_supers()方法、klassItable::setup_itable_offset_table()方法、fill_oop_maps()方法等。另外也更新了_layout_helper中的值,將此類代表的Java類所創建的Java實例的大小更新為info.instance_size,這個值是在之前計算字段布局時計算出來的,這里不再介紹。

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

oop java_lang_Class::create_mirror(KlassHandle k, Handle protection_domain, TRAPS) {
  assert(k->java_mirror() == NULL, "should only assign mirror once");
  // ...

  // Class_klass has to be loaded because it is used to allocate the mirror.
  ////////////////////////////////////////////////////////////////////////////////
  if (SystemDictionary::Class_klass_loaded()) {
	// 注意 allocate_instance 內部會根據 k 中的信息,計算需要分配的空間,包含靜態變量的大小。然后對 mirror 的空間進行分配。
    // Allocate mirror (java.lang.Class instance)
    InstanceMirrorKlass* imk = InstanceMirrorKlass::cast(SystemDictionary::Class_klass());
    Handle               mirror = imk->allocate_instance(k, CHECK_0); // 返回的是instanceOop對象

    // mirror是instanceOop對象,而mirror->klass()就是InstanceMirrorKlass*類型
    InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); // mk代表的是java.lang.Class類
    oop                  moop = mirror(); // moop代表的是java.lang.Class對象
    int                  sofc = mk->compute_static_oop_field_count(moop);
    java_lang_Class::set_static_oop_field_count(moop, sofc);

    // It might also have a component mirror.  This mirror must already exist.
    if (k->oop_is_array()) {       // 數組
      Handle comp_mirror;
      if (k->oop_is_typeArray()) { // 基本類型數組
        BasicType type = TypeArrayKlass::cast(k())->element_type();
        comp_mirror = Universe::java_mirror(type); // oop轉換為Handle類型,會調用轉換構造函數
      } else {                     // 對象類型數組
        assert(k->oop_is_objArray(), "Must be");
        Klass* element_klass = ObjArrayKlass::cast(k())->element_klass();
        assert(element_klass != NULL, "Must have an element klass");
        comp_mirror = element_klass->java_mirror(); // oop轉換為Handle類型,會調用轉換構造函數
      }
      assert(comp_mirror.not_null(), "must have a mirror");

      // Two-way link between the array klass and its component mirror:
      oop tmp = comp_mirror();
      ArrayKlass::cast(k())->set_component_mirror(tmp);
      set_array_klass(tmp, k());
    } else {
      assert(k->oop_is_instance(), "Must be");
      // ...
      // do_local_static_fields 會對靜態字段進行初始化。 注意此是傳入的函數指針表示的 initialize_static_field 函數,
      // do_local_static_fields 會在內部遍歷所有靜態字段,然后調用這個函數對他們進行初始化。
      // Initialize static fields
      InstanceKlass* ik = InstanceKlass::cast(k());
      ik->do_local_static_fields(&initialize_static_field, CHECK_NULL);
    }
    return mirror();
  }
  ////////////////////////////////////////////////////////////////////////////////
  else {
    if (fixup_mirror_list() == NULL) {
      GrowableArray<Klass*>* list = new (ResourceObj::C_HEAP, mtClass) GrowableArray<Klass*>(40, true);
      set_fixup_mirror_list(list);
    }
    GrowableArray<Klass*>* list = fixup_mirror_list();
    Klass* kls = k();
    list->push(kls);
    return NULL;
  }
  ////////////////////////////////////////////////////////////////////////////////
}

由於任何一個Java類都有一個Class對象來表示,所以在創建了表示普通Java類的InstanceKlass對象后,還需要創建對應的InstanceOop對象(代表Class對象)。如果java.lang.Class類還沒有被解析,則將相關信息暫時存儲到數組中,后續在類解析后會做處理,處理邏輯和當前類處理java.lang.Class類被加載時的邏輯基本一致。

調用的InstanceMirrorKlass::allocate_instance()方法創建的表示java中java.lang.Class對象的instanceOop實例。實現如下:

instanceOop InstanceMirrorKlass::allocate_instance(KlassHandle k, TRAPS) {
  // Query before forming handle.
  int          size = instance_size(k);
  KlassHandle  h_k(THREAD, this);
  instanceOop  i = (instanceOop) CollectedHeap::Class_obj_allocate(h_k, size, k, CHECK_NULL);
  return i;
}

oop CollectedHeap::Class_obj_allocate(KlassHandle klass, int size, KlassHandle real_klass, TRAPS) {

  HeapWord* obj;
  obj = common_mem_allocate_init(real_klass, size, CHECK_NULL); // 分配內存並初始化為0

  assert(Universe::is_bootstrapping() || !((oop)obj)->is_array(), "must not be an array");
  oop mirror = (oop)obj;

  java_lang_Class::set_oop_size(mirror, size);

  // Setup indirections
  if (!real_klass.is_null()) {
    java_lang_Class::set_klass(mirror, real_klass());
    real_klass->set_java_mirror(mirror);  
  }

  return mirror;
}

創建出表示了java.lang.Class對象的oop實例后,設置到InstanceKlass實例的_java_mirror屬性中,同時也設置oop的_klass屬性。如果當前類表示數組,那么在java_lang_Class::create_mirror()方法中還會設置表示數組的ArrayKlass對象的_component_mirror屬性,同時也會設置表示當前數組的Class對象的_array_klass屬性。

如果當前類是普通類,那么調用do_local_static_fields()方法,這個方法的實現如下: 

void InstanceKlass::do_local_static_fields(void f(fieldDescriptor*, TRAPS), TRAPS) {
  instanceKlassHandle h_this(THREAD, this);
  do_local_static_fields_impl(h_this, f, CHECK);
}

void InstanceKlass::do_local_static_fields_impl(instanceKlassHandle this_oop, void f(fieldDescriptor* fd, TRAPS), TRAPS) {
  instanceKlassHandle ikh = this_oop();
  for (JavaFieldStream fs(ikh); !fs.done(); fs.next()) {
    if (fs.access_flags().is_static()) { // 只處理靜態字段,因為只有靜態字段的值存儲到Class對象中
       fieldDescriptor& fd = fs.field_descriptor();
       f(&fd, CHECK);
    }
  }
}

調用的initialize_static_field()函數如下:

static void initialize_static_field(fieldDescriptor* fd, TRAPS) {
  InstanceKlass* fh = fd->field_holder();
  oop       tmp = fh->java_mirror();
  Handle    mirror( THREAD,tmp );
  assert(mirror.not_null() && fd->is_static(), "just checking");
  if (fd->has_initial_value()) {
    BasicType t = fd->field_type();
    switch (t) {
      case T_BYTE:
        mirror()->byte_field_put(fd->offset(), fd->int_initial_value());
        break;
      case T_BOOLEAN:
        mirror()->bool_field_put(fd->offset(), fd->int_initial_value());
        break;
      case T_CHAR:
        mirror()->char_field_put(fd->offset(), fd->int_initial_value());
        break;
      case T_SHORT:
        mirror()->short_field_put(fd->offset(), fd->int_initial_value());
        break;
      case T_INT:
        mirror()->int_field_put(fd->offset(), fd->int_initial_value());
        break;
      case T_FLOAT:
        mirror()->float_field_put(fd->offset(), fd->float_initial_value());
        break;
      case T_DOUBLE:
        mirror()->double_field_put(fd->offset(), fd->double_initial_value());
        break;
      case T_LONG:{
        jlong offset = fd->offset();
        jlong vo = fd->long_initial_value();
        oop mr = mirror();
        mr->long_field_put(offset,vo);
        break;
      }
      case T_OBJECT:
        {
          oop string = fd->string_initial_value(CHECK);
          mirror()->obj_field_put(fd->offset(), string);
        }
        break;
      default:
        THROW_MSG(vmSymbols::java_lang_ClassFormatError(),"Illegal ConstantValue attribute in class file");
    }// end switch
  }
}

do_local_static_fields()函數會對靜態字段進行初始化,注意此時傳入的函數指針指向initialize_static_field()函數,do_local_static_fields()會在內部遍歷所有靜態字段,然后調用這個函數對他們進行初始化。 

相關文章的鏈接如下:

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的大小 

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

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

   

 

 

 

 

  

 


免責聲明!

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



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