专注虚拟机与编译器研究

解析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源码剖析系列文章!

   

 

 

 

 

  

 

posted on 2020-08-10 16:28  鸠摩(马智)  阅读(3010)  评论(0编辑  收藏  举报

导航