初始化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源码剖析系列文章!
初始化vtable的更多相关文章
- 初始化itable
在InstanceKlass::link_class_impl()方法中完成方法连接后会继续初始化vtable与itable,之前已经介绍过vtable与itable,并且在类解析过程中已经完成了大小 ...
- [Inside HotSpot] Java的方法调用
1. 方法调用模块入口 Java所有的方法调用都会经过JavaCalls模块.该模块又细分为call_virtual调用虚函数,call_static调用静态函数等.虚函数调用会根据对象类型进行方法决 ...
- HotSpot的执行引擎-CallStub栈帧
之前多次提到接触到调用JavaCalls::call()方法来执行Java方法,如: (1)Java主类装载时,调用JavaCalls::call()方法执行的Java方法checkAndLoadMa ...
- Java引用类型之软引用(1)
Java使用SoftReference来表示软引用,软引用是用来描述一些“还有用但是非必须”的对象.对于软引用关联着的对象,在JVM应用即将发生内存溢出异常之前,将会把这些软引用关联的对象列进去回收对 ...
- Java引用类型之软引用(2)
下面接着上一篇介绍第2阶段和第3阶段的处理逻辑. 2.process_phase2() 第2个阶段移除所有的referent还存活的Reference,也就是从refs_list中移除Referenc ...
- Java引用类型之弱引用与幻像引用
这一篇将介绍弱引用和幻像引用. 1.WeakReference WeakReference也就是弱引用,弱引用和软引用类似,它是用来描述"非必须"的对象的,它的强度比软引用要更弱一 ...
- Java引用类型之最终引用
FinalReference类只有一个子类Finalizer,并且Finalizer由关键字final修饰,所以无法继承扩展.类的定义如下: class FinalReference<T> ...
- JVM的方法执行引擎-entry point栈帧
接着上一篇去讲,回到JavaCalls::call_helper()中: address entry_point = method->from_interpreted_entry(); entr ...
- JVM的方法执行引擎-模板表
Java的模板解析执行需要模板表与转发表的支持,而这2个表中的数据在HotSpot虚拟机启动时就会初始化.这一篇首先介绍模板表. 在启动虚拟机阶段会调用init_globals()方法初始化全局模块, ...
随机推荐
- Python虚拟环境(virtualenv)
python虚拟环境 虚拟环境:一个独立的可以运行的python执行环境,可以创建多个,且相互之间互不影响 使用virtualenv库 pip install virtualenv 用法 # 创建虚拟 ...
- MySQL中的循环
MySQL中的三中循环 while . loop .repeat 求 1-n 的和第一种 while 循环 : /* while循环语法: while 条件 DO 循环体; end while; */ ...
- [jvm] -- 类文件结构篇
类文件结构 结构图 魔数 头四个字节,作用是确定这个文件是否为一个能被虚拟机接收的 Class 文件. Class 文件版本 第五和第六是次版本号,第七和第八是主版本号. 高版本的 Java 虚拟机 ...
- vue组件库用markdown生成文档
前言: 开发vue组件库需要提供组件的使用文档,最好是有渲染到浏览器的demo实例,既能操作又能查看源代码.markdown作为常用的文档编写载体,如果能在里面直接写vue组件,同时编写使用说明就再好 ...
- Python灰帽子:黑客与逆向工程师的Python编程之道PDF高清完整版免费下载|百度云盘
百度云盘免费下载:Python灰帽子:黑客与逆向工程师的Python编程之道PDF高清完整版免费下载 提取码:8nki 目录 · · · · · · 第1章 搭建开发环境 11.1 操作系统要求 1 ...
- Go语言系列之手把手教你撸一个ORM(一)
项目地址:https://github.com/yoyofxteam/yoyodata 欢迎星星,感谢 前言:最近在学习Go语言,就出于学习目的手撸个小架子,欢迎提出宝贵意见,项目使用Mysql数据库 ...
- 04爬取拉勾网Python岗位分析报告
# 导入需要的包import requestsimport time,randomfrom openpyxl import Workbookimport pymysql.cursors#@ 连接数据库 ...
- OpenLDAP on Centos7
一.环境准备 echo nameserver 114.114.114.114 > /etc/resolv.conf ##更改DNSecho 192.168.0.190 hello.com > ...
- Springboot 多数据源配置,结合tk-mybatis
一.前言 作为一个资深的CRUD工程师,我们在实际使用springboot开发项目的时候,难免会遇到同时使用多个数据库的情况,比如前脚刚查询mysql,后脚就要查询sqlserver. 这时,我们很直 ...
- PHP floatval()、doubleval () 函数
floatval 函数用于获取变量的浮点值. floatval 不能用于数组或对象.高佣联盟 www.cgewang.com 版本要求:PHP 4 >= 4.2.0, PHP 5, PHP 7. ...