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. SQL 给某字段添加汉字却显示??

    错误展示: 解决方案: 1.在要修改的数据库上单击鼠标右键,并选择“属性”.   2.在弹出的数据库属性窗口中点击“选择页”中的“选项”.   3.将排序规则由默认的SQL_Latin1_Genera ...

  2. 题解 洛谷 P4177 【[CEOI2008]order】

    进行分析后,发现最大收益可以转化为最小代价,那么我们就可以考虑用最小割来解决这道题. 先算出总收益\(sum\),总收益减去最小代价即为答案. 然后考虑如何建图,如何建立最小割的模型. 发现一个任务最 ...

  3. eclipse GIT本地库分支操作

    git分支是一个重要的知识点,平时我们开发主要结合eclipse,idea来操作,今天这贴主要以eclipse来操作git本地库分支,主要内容包括新建分支,切换分支,合并分支,冲突解决,重命名分支,删 ...

  4. vue学习(九) 使用内联样式设置style样式

    /html <div id="app"> //对象就是无序键值对的集合 <h1 :style="{ color:red, 'font-weight':2 ...

  5. vue学习(二) 三个指令v-cloak v-text v-html

    //style <style> [v-cloak]{ display:none } </style> //html <div id="app"> ...

  6. ASP.NET Core 监听SQL Server数据库的实时信息

    1.开发环境: 开发工具:Visual Studio 2019 数据库:SQL Server2012 开发环境:.Net Core 3.1 2.使用技术: Signalr:实现消息推送 SqlDepe ...

  7. PHP 命名空间(namespace)定义

    PHP 命名空间(namespace) PHP 命名空间(namespace)是在PHP 5.3中加入的,如果你学过C#和Java,那命名空间就不算什么新事物. 不过在PHP当中还是有着相当重要的意义 ...

  8. PHP imagecolorclosesthwb - 取得与指定的颜色最接近的色度的黑白色的索引

    imagecolorclosesthwb — 取得与指定的颜色最接近的色度的黑白色的索引.高佣联盟 www.cgewang.com 语法 int imagecolorclosesthwb (s res ...

  9. JS时间和时间戳的转换

    时间转为时间戳 timeToTimestamp(time){ let timestamp = Date.parse(time) return timestamp; } 时间戳转为本地时间 timest ...

  10. tensorflow2.0 实现gpu和cpu切换

      昨天把GPU版本的tf2.0 安装成功之后,现在所有的代码运行居然都在gpu上跑了,并且在对gpu使用情况没有限制的条件下,既然gpu内存跑满了,代码就崩了怎么样才能随心所欲的指定代码是在cpu还 ...