解析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的源代码
13、类加载器
14、类的双亲委派机制
15、核心类的预装载
16、Java主类的装载
17、触发类的装载
18、类文件介绍
19、文件流
20、解析Class文件
21、常量池解析(1)
22、常量池解析(2)
23、字段解析(1)
24、字段解析之伪共享(2)
25、字段解析(3)
28、方法解析
29、klassVtable与klassItable类的介绍
30、计算vtable的大小
31、计算itable的大小
作者持续维护的个人博客 classloading.com。
关注公众号,有HotSpot源码剖析系列文章!
解析Class文件之创建InstanceKlass对象的更多相关文章
- sax解析xml文件,封装到对象中
创建User.java类 public class User { private String id; private String name; private String age; private ...
- Java&Xml教程(五)使用SAX方式解析XML文件
Java SAX解析机制为我们提供了一系列的API来处理XML文件,SAX解析和DOM解析方式不太一样,它并不是將XML文件内容一次性全部加载,而是连续的部分加载. javax.xml.parsers ...
- XML专题:使用NSXMLParser解析xml文件
使用NSXMLParser解析xml文件 1. 设置委托对象,开始解析 NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data ...
- 遍历文件 创建XML对象 方法 python解析XML文件 提取坐标计存入文件
XML文件??? xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 里面的标签都是可以随心所欲的按照他的命名规则来定义的,文件名为roi.xm ...
- Android -- 创建XML文件对象及其序列化, pull解析XML文件
1. 创建XML文件对象及其序列化 示例代码:(模拟以xml格式备份短信到SD卡) SmsInfo.java, bean对象 /** * 短信的业务bean * @author Administrat ...
- 使用dom4j创建和解析xml文件
使用dom4j创建和解析xml文件 在项目开发中,我们经常会遇到xml文件的创建和解析从别人接口得到的xml文件,而我们最常使用的组件是dom4j. 下面我就以代码来讲解一下如何使用dom4j来创建x ...
- Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...
- 创建xml文件、解析xml文件
1.创建XML文件: import codecs import xml.dom.minidom doc=xml.dom.minidom.Document() print doc root=do ...
- 使用XPath对象解析xml文件
使用XPath对象解析xml文件 1.DocumentBuilderFactory类 工厂API,使应用程序能从XML文档获取生成DOM对象树的解析器 其构造方法受保护,用newInstance() ...
随机推荐
- SQL 给某字段添加汉字却显示??
错误展示: 解决方案: 1.在要修改的数据库上单击鼠标右键,并选择“属性”. 2.在弹出的数据库属性窗口中点击“选择页”中的“选项”. 3.将排序规则由默认的SQL_Latin1_Genera ...
- 题解 洛谷 P4177 【[CEOI2008]order】
进行分析后,发现最大收益可以转化为最小代价,那么我们就可以考虑用最小割来解决这道题. 先算出总收益\(sum\),总收益减去最小代价即为答案. 然后考虑如何建图,如何建立最小割的模型. 发现一个任务最 ...
- eclipse GIT本地库分支操作
git分支是一个重要的知识点,平时我们开发主要结合eclipse,idea来操作,今天这贴主要以eclipse来操作git本地库分支,主要内容包括新建分支,切换分支,合并分支,冲突解决,重命名分支,删 ...
- vue学习(九) 使用内联样式设置style样式
/html <div id="app"> //对象就是无序键值对的集合 <h1 :style="{ color:red, 'font-weight':2 ...
- vue学习(二) 三个指令v-cloak v-text v-html
//style <style> [v-cloak]{ display:none } </style> //html <div id="app"> ...
- ASP.NET Core 监听SQL Server数据库的实时信息
1.开发环境: 开发工具:Visual Studio 2019 数据库:SQL Server2012 开发环境:.Net Core 3.1 2.使用技术: Signalr:实现消息推送 SqlDepe ...
- PHP 命名空间(namespace)定义
PHP 命名空间(namespace) PHP 命名空间(namespace)是在PHP 5.3中加入的,如果你学过C#和Java,那命名空间就不算什么新事物. 不过在PHP当中还是有着相当重要的意义 ...
- PHP imagecolorclosesthwb - 取得与指定的颜色最接近的色度的黑白色的索引
imagecolorclosesthwb — 取得与指定的颜色最接近的色度的黑白色的索引.高佣联盟 www.cgewang.com 语法 int imagecolorclosesthwb (s res ...
- JS时间和时间戳的转换
时间转为时间戳 timeToTimestamp(time){ let timestamp = Date.parse(time) return timestamp; } 时间戳转为本地时间 timest ...
- tensorflow2.0 实现gpu和cpu切换
昨天把GPU版本的tf2.0 安装成功之后,现在所有的代码运行居然都在gpu上跑了,并且在对gpu使用情况没有限制的条件下,既然gpu内存跑满了,代码就崩了怎么样才能随心所欲的指定代码是在cpu还 ...