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

 

 

  

解析Class文件之创建InstanceKlass对象的更多相关文章

  1. sax解析xml文件,封装到对象中

    创建User.java类 public class User { private String id; private String name; private String age; private ...

  2. Java&Xml教程(五)使用SAX方式解析XML文件

    Java SAX解析机制为我们提供了一系列的API来处理XML文件,SAX解析和DOM解析方式不太一样,它并不是將XML文件内容一次性全部加载,而是连续的部分加载. javax.xml.parsers ...

  3. XML专题:使用NSXMLParser解析xml文件

    使用NSXMLParser解析xml文件 1. 设置委托对象,开始解析     NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data ...

  4. 遍历文件 创建XML对象 方法 python解析XML文件 提取坐标计存入文件

    XML文件??? xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 里面的标签都是可以随心所欲的按照他的命名规则来定义的,文件名为roi.xm ...

  5. Android -- 创建XML文件对象及其序列化, pull解析XML文件

    1. 创建XML文件对象及其序列化 示例代码:(模拟以xml格式备份短信到SD卡) SmsInfo.java, bean对象 /** * 短信的业务bean * @author Administrat ...

  6. 使用dom4j创建和解析xml文件

    使用dom4j创建和解析xml文件 在项目开发中,我们经常会遇到xml文件的创建和解析从别人接口得到的xml文件,而我们最常使用的组件是dom4j. 下面我就以代码来讲解一下如何使用dom4j来创建x ...

  7. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  8. 创建xml文件、解析xml文件

        1.创建XML文件: import codecs import xml.dom.minidom doc=xml.dom.minidom.Document() print doc root=do ...

  9. 使用XPath对象解析xml文件

    使用XPath对象解析xml文件 1.DocumentBuilderFactory类  工厂API,使应用程序能从XML文档获取生成DOM对象树的解析器 其构造方法受保护,用newInstance() ...

随机推荐

  1. 搞定 CompletableFuture,并发异步编程和编写串行程序还有什么区别?你们要的多图长文

    你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If you can NOT explain it simply, you do NOT understand it well enough ...

  2. java 将整型数组转化为字符串

    java arrays 和arrayList 的区别 package com.vc; import java.util.Arrays; public class Demo05 { public sta ...

  3. Hbase1.2.3安装

    HBase是一个分布式,版本化,面向列的数据库,基于Google BigTable模型开发的,典型的key/value系统:构建在HDFS上的分布式列存储系统: 在hadoop master1上安装 ...

  4. Spring main方法中怎么调用Dao层和Service层的方法

    在web环境中,一般serviceImpl中的dao之类的数据库连接都由容器启动的时候创建好了,不会报错.但是在main中,没有这个环境,所以需要获取环境: ApplicationContext ct ...

  5. 构建私有的verdaccio npm服务

    用了很长一段时间的cnpmjs做库私有库,发现两个问题 1. 最开始是mysql对表情emoij的支持不好,但由于数据库没办法调整所以只好把第三方库都清掉,只留私有库 2. mac 上面cnpm in ...

  6. Java-每日学习笔记(数据库与idea技巧)

    Java杂记-2020.07.28 简单记录下今天项目用到的东西还有技术公众号学到的一些知识点 Java事务 idea编码技巧 数据库快速插入100万条数据 Java实现sql回滚 Java事务 事务 ...

  7. PHP timezone_location_get() 函数

    ------------恢复内容开始------------ 实例 返回指定时区的位置信息: <?php$tz=timezone_open("Asia/Taipei");ec ...

  8. PHP set_exception_handler() 函数

    定义和用法 set_exception_handler() 函数设置用户自定义的异常处理函数. 该函数用于创建运行期间的用户自己的异常处理方法. 该函数返回旧的异常处理程序,如果失败则返回 NULL. ...

  9. PHP linkinfo() 函数

    定义和用法 linkinfo() 函数返回有关一个硬连接的信息. 该函数返回设备 ID,如果失败则返回 FALSE. 语法 linkinfo(path) 参数 描述 path 必需.规定要检查的路径. ...

  10. PHP xml_set_processing_instruction_handler() 函数

    定义和用法 xml_set_processing_instruction_handler() 函数规定当解析器在 XML 文档中找到处理指令时被调用的函数. 处理指令包含在 <? 和 ?> ...