初始化vtable
在InstanceKlass::link_class_impl()方法中完成方法连接后会继续初始化vtable与itable,之前已经介绍过vtable与itable,并且在类解析过程中已经完成了大小的计算并且也为相关信息的存储开辟了对应的内存空间,也就是在InstanceKlass本身需要占用的内存空间之后紧接着存储vtable,vtable后接着存储itable。
InstanceKlass::link_class_impl()方法中相关的调用语句如下:
if (!this_oop()->is_shared()) {
ResourceMark rm(THREAD);
klassVtable* kv = this_oop->vtable();
kv->initialize_vtable(true, CHECK_false); klassItable* ki = this_oop->itable();
ki->initialize_itable(true, CHECK_false);
}
调用的vtable()函数的实现如下:
klassVtable* InstanceKlass::vtable() const {
intptr_t* base = start_of_vtable();
int length = vtable_length() / vtableEntry::size();
return new klassVtable(this, base, length);
}
start_of_vtable()就是获取vtable的起始地址,因为vtable存储在紧跟InstanceKlass本身占用的内存之后,所以可以轻易获取。vtable_length()就是获取InstanceKlass对象中的_vtable_len属性的值,这个值在解析Class文件、创建InstanceKlass对象时已经计算好,这里只需要获取即可。
之前已经介绍过klassVtable类,下面再简单介绍一下:
class klassVtable : public ResourceObj {
KlassHandle _klass; // my klass
int _tableOffset; // offset of start of vtable data within klass
int _length; // length of vtable (number of entries)
// ... public:
klassVtable(KlassHandle h_klass, void* base, int length) : _klass(h_klass) {
_tableOffset = (address)base - (address)h_klass();
_length = length;
}
// ...
}
可以看到,调用的构造函数中会初始化_tableOffset与_length。_tableOffset就是相对偏移量,length就是vtableEntry的数量。
获取到klassVtable对象后,在InstanceKlass::link_class_impl()方法中调用klassVtable对象的initialize_vtable()函数进行虚函数表的初始化,方法的实现如下:
// Revised(修改) lookup semantics introduced 1.3 (Kestrel beta)
void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) { // Note: Arrays can have intermediate(中间的) array supers. Use java_super to skip them.
KlassHandle super(THREAD, klass()->java_super());
int nofNewEntries = 0;
// ... int super_vtable_len = initialize_from_super(super);
//////////////////////////////////////////////////////////////////////////////
if (klass()->oop_is_array()) {
assert(super_vtable_len == _length, "arrays shouldn't introduce new methods");
}
//////////////////////////////////////////////////////////////////////////////
else {
assert(_klass->oop_is_instance(), "must be InstanceKlass");
InstanceKlass* ikl = ik();
Array<Method*>* methods = ikl->methods();
int len = methods->length();
int initialized = super_vtable_len; // 第1部分
// Check each of this class's methods against super;
// if override, replace in copy of super vtable, otherwise append to end
for (int i = 0; i < len; i++) {
// update_inherited_vtable can stop for gc - ensure using handles
HandleMark hm(THREAD);
assert(methods->at(i)->is_method(), "must be a Method*");
methodHandle methodH(THREAD, methods->at(i));
InstanceKlass* instanceK = ik();
bool needs_new_entry = update_inherited_vtable(instanceK, methodH, super_vtable_len, -1, checkconstraints, CHECK);
if (needs_new_entry) {
put_method_at(methodH(), initialized);
methodH()->set_vtable_index(initialized); // set primary vtable index
initialized++;
}
} // 第2部分
// update vtable with default_methods
Array<Method*>* default_methods = ik()->default_methods();
if (default_methods != NULL) {
len = default_methods->length();
if (len > 0) {
Array<int>* def_vtable_indices = NULL;
if ( (def_vtable_indices = ik()->default_vtable_indices()) == NULL ) {
def_vtable_indices = ik()->create_new_default_vtable_indices(len, CHECK);
} else {
assert(def_vtable_indices->length() == len, "reinit vtable len?");
}
for (int i = 0; i < len; i++) {
HandleMark hm(THREAD);
assert(default_methods->at(i)->is_method(), "must be a Method*");
methodHandle mh(THREAD, default_methods->at(i)); bool needs_new_entry = update_inherited_vtable(ik(), mh, super_vtable_len, i, checkconstraints, CHECK); // needs new entry
if (needs_new_entry) {
put_method_at(mh(), initialized);
def_vtable_indices->at_put(i, initialized); //set vtable index
initialized++;
}
}
} // end if(len > 0)
} // 第3部分
// add miranda methods; it will also return the updated initialized
// Interfaces do not need interface methods in their vtables
// This includes miranda methods and during later processing, default methods
if (!ik()->is_interface()) {
initialized = fill_in_mirandas(initialized);
} // In class hierarchies where the accessibility is not increasing (i.e., going from private ->
// package_private -> public/protected), the vtable might actually be smaller than our initial
// calculation.
assert(initialized <= _length, "vtable initialization failed");
for(;initialized < _length; initialized++) {
put_method_at(NULL, initialized);
}
}
//////////////////////////////////////////////////////////////////////////////
}
如果当前类是非数组,那么主要执行的逻辑分3个部分,对当前类或接口中定义的普通方法、默认方法以及miranda方法的处理。
调用的initialize_from_super()函数的实现如下:
// Copy super class's vtable to the first part (prefix) of this class's vtable,
// and return the number of entries copied. Expects that 'super' is the Java
// super class (arrays can have "array" super classes that must be skipped).
int klassVtable::initialize_from_super(KlassHandle super) {
if (super.is_null()) { // Object没有父类,所以直接返回
return 0;
} else {
// copy methods from superKlass
// can't inherit from array class, so must be InstanceKlass
assert(super->oop_is_instance(), "must be instance klass");
InstanceKlass* sk = (InstanceKlass*)super();
klassVtable* superVtable = sk->vtable();
assert(superVtable->length() <= _length, "vtable too short"); vtableEntry* vte = table();
superVtable->copy_vtable_to(vte); return superVtable->length();
}
}
将父类的vtable拷贝一份存储到子类vtable的前面,以完成继承。调用的vtable()与table()方法的实现如下:
klassVtable* InstanceKlass::vtable() const {
intptr_t* base = start_of_vtable();
int length = vtable_length() / vtableEntry::size();
return new klassVtable(this, base, length);
} vtableEntry* table() const{
return (vtableEntry*)( address(_klass()) + _tableOffset );
}
通过_tableOffset偏移来获取父类vtable的首地址,然后通过klassVtable对象来操作vtable中含有的vtableEntry。
// Copy this class's vtable to the vtable beginning at start.
// Used to copy superclass vtable to prefix of subclass's vtable.
void klassVtable::copy_vtable_to(vtableEntry* start) {
Copy::disjoint_words(
(HeapWord*)table(),
(HeapWord*)start,
_length * vtableEntry::size()
);
} // Word-aligned words, disjoint, not atomic on each word
static void disjoint_words(HeapWord* from, HeapWord* to, size_t count) {
assert_params_ok(from, to, LogHeapWordSize);
assert_disjoint(from, to, count);
pd_disjoint_words(from, to, count);
} static void pd_disjoint_words(HeapWord* from, HeapWord* to, size_t count) {
switch (count) {
case 8: to[7] = from[7];
case 7: to[6] = from[6];
case 6: to[5] = from[5];
case 5: to[4] = from[4];
case 4: to[3] = from[3];
case 3: to[2] = from[2];
case 2: to[1] = from[1];
case 1: to[0] = from[0];
case 0: break;
default:
(void)memcpy(to, from, count * HeapWordSize);
break;
}
}
方法的实现还算简单,为了高效进行拷贝,方法还采用了一些技巧。
1、对普通方法的处理
在klassVtable::initialize_vtable()方法中,拷贝完父类vtable后,接下来就是遍历当前类中的方法,然后更新或填充自己的vtable了。循环处理当前类中定义的普通方法,通过调用update_inherited_vtable()方法来判断,到底是更新父类对应的vtableEntry还是新添加一个vtableEntry,方法的实现如下:
// Update child's copy of super vtable for overrides
// OR return true if a new vtable entry is required.
// Only called for InstanceKlass's, i.e. not for arrays
// If that changed, could not use _klass as handle for klass
bool klassVtable::update_inherited_vtable(
InstanceKlass* klass,
methodHandle target_method,
int super_vtable_len,
int default_index,
bool checkconstraints, TRAPS
){
ResourceMark rm;
bool allocate_new = true;
assert(klass->oop_is_instance(), "must be InstanceKlass"); Array<int>* def_vtable_indices = NULL;
bool is_default = false;
// default methods are concrete methods in superinterfaces which are added to the vtable
// with their real method_holder
// Since vtable and itable indices share the same storage, don't touch
// the default method's real vtable/itable index
// default_vtable_indices stores the vtable value relative to this inheritor
if (default_index >= 0 ) {
is_default = true;
def_vtable_indices = klass->default_vtable_indices();
assert(def_vtable_indices != NULL, "def vtable alloc?");
assert(default_index <= def_vtable_indices->length(), "def vtable len?");
} else { // 在处理类中定义的普通方法时,default_index的值为-1,执行这个分支的逻辑
assert(klass == target_method()->method_holder(), "caller resp.");
// Initialize the method's vtable index to "nonvirtual".
// If we allocate a vtable entry, we will update it to a non-negative number.
target_method()->set_vtable_index(Method::nonvirtual_vtable_index);
} // Static and <init> methods are never in
if (target_method()->is_static() || target_method()->name() == vmSymbols::object_initializer_name()) {
return false;
} // 执行这里的代码时,说明方法为非静态方法、非<init>方法
if (target_method->is_final_method(klass->access_flags())) {
// a final method never needs a new entry; final methods can be statically
// resolved and they have to be present in the vtable only if they override
// a super's method, in which case they re-use its entry
allocate_new = false;
} else if (klass->is_interface()) {
// 当klass为接口时,allocate_new的值会更新为false,也就是接口中的方法不需要分配vtableEntry
allocate_new = false; // see note below in needs_new_vtable_entry
// An interface never allocates new vtable slots, only inherits old ones.
// This method will either be assigned its own itable index later,
// or be assigned an inherited vtable index in the loop below.
// default methods inherited by classes store their vtable indices
// in the inheritor's default_vtable_indices
// default methods inherited by interfaces may already have a
// valid itable index, if so, don't change it
// overpass methods in an interface will be assigned an itable index later
// by an inheriting class
if (!is_default || !target_method()->has_itable_index()) {
target_method()->set_vtable_index(Method::pending_itable_index);
}
} // we need a new entry if there is no superclass
if (klass->super() == NULL) {
return allocate_new;
} // private methods in classes always have a new entry in the vtable
// specification interpretation since classic has
// private methods not overriding
// JDK8 adds private methods in interfaces which require invokespecial
if (target_method()->is_private()) {
return allocate_new;
} // search through the vtable and update overridden entries
// Since check_signature_loaders acquires SystemDictionary_lock
// which can block for gc, once we are in this loop, use handles
// For classfiles built with >= jdk7, we now look for transitive overrides Symbol* name = target_method()->name();
Symbol* signature = target_method()->signature(); KlassHandle target_klass(THREAD, target_method()->method_holder());
if (target_klass == NULL) {
target_klass = _klass;
} Handle target_loader(THREAD, target_klass->class_loader());
Symbol* target_classname = target_klass->name(); ///////////////////////////////////////////////////////////////////////
for(int i = 0; i < super_vtable_len; i++) { Method* super_method = method_at(i);
// Check if method name matches
if (super_method->name() == name && super_method->signature() == signature) {
// get super_klass for method_holder for the found method
InstanceKlass* super_klass = super_method->method_holder();
if( is_default ||
(
// 判断super_klass中的super_method方法是否可以被重写,如果可以,则返回true
(super_klass->is_override(super_method, target_loader, target_classname, THREAD)) ||
(
// 方法可能重写了间接父类
( klass->major_version() >= VTABLE_TRANSITIVE_OVERRIDE_VERSION ) &&
( (super_klass = find_transitive_override(super_klass,target_method, i, target_loader,target_classname, THREAD))!= (InstanceKlass*)NULL )
)
)
){
// overriding, so no new entry
allocate_new = false; put_method_at(target_method(), i);
if (!is_default) {
target_method()->set_vtable_index(i);
} else {
if (def_vtable_indices != NULL) {
def_vtable_indices->at_put(default_index, i);
}
assert(super_method->is_default_method() ||
super_method->is_overpass() ||
super_method->is_abstract(), "default override error");
}
} else {
// allocate_new = true; default. We might override one entry,
// but not override another. Once we override one, not need new
}
}
}
///////////////////////////////////////////////////////////////////////
return allocate_new;
}
举个例子如下:
public abstract class TestVtable {
public void md2(){}
}
TestVtable中会遍历到3个方法:
- <init>,可以看到update_inherited_vtable()方法对vmSymbols::object_initializer_name()名称的方法的处理是直接返回false,表示不需要新的vtableEntry;
- md2(),这是类TestVtable中定义的方法,会临时给对应的Method::_vtable_index赋值为Method::nonvirtual_vtable_index,然后遍历父类,看是否定义了名称name和签名signature相同的方法,如果有,很可能不需要新的vtableEntry,只需要更新已有的vtableEntry即可。由于TestVtable的默认父类为Object,Object中总共有5个方法会存储到vtable,分别为:finalize()、equals()、toString()、hashCode()和clone(),很明显md2()并没有重写父类方法,直接返回true,表示需要为此方法新增一个vtableEntry。这样Method::vtable_index的值会更新为initialized,也就是下标索引为5的地方将存储md2()方法,下标索引从0开始。
再举个例子如下:
public abstract class TestVtable {
public String toString(){
return "TestVtable";
}
}
此类的方法共有2个,<init>与toString(),<init>不需要vtableEntry,toString重写了Object类中的toString(),所以也不需要新的vtableEntry。toString()是可被重写的,调用is_override()方法判断,实现如下:
// Returns true iff super_method can be overridden by a method in targetclassname
// See JSL 3rd edition 8.4.6.1
// Assumes name-signature match
// "this" is InstanceKlass of super_method which must exist
// note that the InstanceKlass of the method in the targetclassname has not always been created yet
bool InstanceKlass::is_override(methodHandle super_method, Handle targetclassloader, Symbol* targetclassname, TRAPS) {
// Private methods can not be overridden
if (super_method->is_private()) {
return false;
}
// If super method is accessible, then override
if ((super_method->is_protected()) ||
(super_method->is_public())) {
return true;
}
// Package-private methods are not inherited outside of package
assert(super_method->is_package_private(), "must be package private");
return(is_same_class_package(targetclassloader(), targetclassname));
}
调用is_same_class_package()主要处理如下的情况:
在com.test中定义TestVtable类,如下:
package com.test; import com.test2.CB; public abstract class TestVtable extends CB{
public void md(){}
}
在com.test中定义CA类,如下:
package com.test; public class CA{
void md() { }
}
在com.test2中定义CB类,如下:
package com.test2; import com.test.CA; public class CB extends CA{
private void md(){}
}
调用的put_method_at()方法更新父类的vtableEntry,此方法的实现如下:
void klassVtable::put_method_at(Method* m, int index) {
vtableEntry* vte = table();
vte[index].set(m);
}
其实就是更新当前类对应位置上的vtableEntry。
2、默认方法的处理
方法klassVtable::initialize_vtable()第2部分代码是对接口中定义的默认方法进行处理。同样会调用update_inherited_vtable()方法判断默认方法是否需要新的vtableEntry,不过传入的default_index的值是大于等于0的。举个例子如下:
interface IA{
default void test(){ }
} public abstract class TestVtable implements IA{ }
在处理TestVtable时,有一个默认的方法test(),由于表示当前类的InstanceKlass对象的_default_vtable_indices属性为NULL,所以首先会调用create_new_vtable_indices()方法根据默认方法的数量len初始化属性,如下:
// create a new array of vtable_indices for default methods
Array<int>* InstanceKlass::create_new_default_vtable_indices(int len, TRAPS) {
// 为什么还要有ClassLoaderData???
Array<int>* vtable_indices = MetadataFactory::new_array<int>(class_loader_data(), len, CHECK_NULL);
assert(default_vtable_indices() == NULL, "only create once");
set_default_vtable_indices(vtable_indices);
return vtable_indices;
}
调用update_inherited_vtable()方法,传入的default_index的值为0。由于没有重写任何父类方法,所以方法返回true,表示需要一个新的vtableEntry,不过还需要在InstanceKlass::_default_vtable_indices属性中记录映射关系,也就是说第0个默认方法要存储到下标索引为5的vtableEntry上,这个记录关系在后面会看到相关应用。
3、miranda方法的处理
方法klassVtable::initialize_vtable()第3部分代码是对miranda方法的处理。举个例子如下:
interface IA{
int md();
} public abstract class TestVtable implements IA {}
会调用fill_in_mirandas()处理miranda方法md(),方法的实现如下:
// Discover miranda methods ("miranda" = "interface abstract, no binding"),
// and append them into the vtable starting at index initialized,
// return the new value of initialized.
// Miranda methods use vtable entries, but do not get assigned a vtable_index
// The vtable_index is discovered by searching from the end of the vtable
int klassVtable::fill_in_mirandas(int initialized) {
GrowableArray<Method*> mirandas(20);
get_mirandas( &mirandas, NULL,
ik()->super(),
ik()->methods(),
ik()->default_methods(),
ik()->local_interfaces());
for (int i = 0; i < mirandas.length(); i++) {
if (PrintVtables && Verbose) {
Method* meth = mirandas.at(i);
ResourceMark rm(Thread::current());
if (meth != NULL) {
char* sig = meth->name_and_sig_as_C_string();
tty->print("fill in mirandas with %s index %d, flags: ",sig, initialized);
meth->access_flags().print_on(tty);
if (meth->is_default_method()) {
tty->print("default ");
}
tty->cr();
}
}
put_method_at(mirandas.at(i), initialized);
++initialized;
}
return initialized;
}
调用的get_mirandas()方法在计算vtable的大小时详细介绍过,这里不再介绍。对于如上实例来说,TestVtable类没有实现IA接口中定义的md()方法,所以说会添加到fill_in_mirandas()方法中定义的mirandas数组中。最后调用put_method_at()方法将miranda方法存放到下标索引为5的vtableEntry中。
相关文章的链接如下:
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的大小
32、解析Class文件之创建InstanceKlass对象
33、字段解析之字段注入
34、类的连接
35、类的连接之验证
36、类的连接之重写(1)
37、类的连接之重写(2)
38、方法的连接
作者持续维护的个人博客 classloading.com。
关注公众号,有HotSpot源码剖析系列文章!
最新文章
- 夺命雷公狗----Git---5---分支
- C++ 11 Template ... 与Decltype 测试
- oracle 方向及资料
- UWP开发中的方向传感器
- 清理一下电脑垃圾,打开Eclipse发现左边的全部项目消失了
- RabbitMQ+PHP 消息队列环境配置
- Linux 系统结构详解
- [转贴]sizeof 和strlen的区别
- bzoj 1500: [NOI2005]维修数列 splay
- Windows消息传递函数SendMessage参数属性
- js中设置setInterval的注意点
- oracle逻辑导入小错:提示无法创建日志提示
- C#设计模式之六原型模式(Prototype)【创建型】
- 快速从一个空虚拟机,空idea打通提交spark
- Lists.newArrayList的一个小坑
- 第2章 细说Linux系统用户/组管理(1)
- 以选项卡的故事扯扯js面向对象
- Hibernate_day03
- PHP扩展类ZipArchive实现压缩解压Zip文件和文件打包下载
- abap 从屏幕输入数据
热门文章
- [jvm] -- 垃圾收集器篇
- git 缓存密码 unable to access... 403错误
- 解决android studio Gradle无法同步问题
- 风速风向 UV 相互转换
- VulnHub靶场学习_HA: Pandavas
- 企业权限管理(SSM整合)(总结)
- MYSQL的事物四大特性
- Skill 脚本演示 ycAutoSnap.skl
- 5.10 省选模拟赛 tree 树形dp 逆元
- 【问题记录】springMVC @Valid使用不生效问题