计算itable的大小
在ClassFileParser::parseClassFile()函数中计算vtable和itable所需要的大小,之前已经介绍过vtable大小的计算,这一篇将详细介绍itable大小的计算过程。调用语句如下:
// Size of Java itable (in words)
if( access_flags.is_interface() ){
itable_size = 0 ; // 当为接口时,itable_size为0
}else{
itable_size = klassItable::compute_itable_size(_transitive_interfaces);
}
由于ClassFileparser::parseClassFile()方法可能分析的是接口,所以要判断一下,当为接口时,并不存在itable,也就是只有类才有itable。调用KlassItable::compute_itable_size()函数计算所需要的大小,此方法的实现如下:
int klassItable::compute_itable_size(Array<Klass*>* transitive_interfaces) {
// Count no of interfaces and total number of interface methods
CountInterfacesClosure cic;
visit_all_interfaces(transitive_interfaces, &cic);
// There's alway an extra itable entry so we can null-terminate it.
int itable_size = calc_itable_size(cic.nof_interfaces() + 1, cic.nof_methods());
return itable_size;
}
调用visit_all_interfaces()函数计算类实现的所有接口的总数(包括直接与间接实现的接口)和接口中定义的所有方法,并通过CountInterfacesClosure类的_nof_interfaces与_nof_methods属性来保存。调用的visit_all_interfaces()函数的实现如下:
// Visit all interfaces with at least one itable method
void visit_all_interfaces(Array<Klass*>* transitive_intf, InterfaceVisiterClosure *blk) {
// Handle array argument
for(int i = 0; i < transitive_intf->length(); i++) {
Klass* intf = transitive_intf->at(i); // Find no. of itable methods
int method_count = 0; // 将Klass类型的intf转换为InstanceKlass类型后调用methods()方法
Array<Method*>* methods = InstanceKlass::cast(intf)->methods();
if (methods->length() > 0) {
for (int i = methods->length(); --i >= 0; ) {
if (interface_method_needs_itable_index(methods->at(i))) {
method_count++;
}
}
} // Only count interfaces with at least one method
if (method_count > 0) {
blk->doit(intf, method_count);
}
}
}
循环遍历每个接口中的每个方法,并调用interface_method_needs_itable_index()方法判断接口中声明的方法是否需要在itable中添加一个新的itableEntry(指itableOffsetEntry和itableMethodEntry)。如果当前接口中有方法需要新的itableEntry,那么会调用CountInterfacesClosure类中的doit()方法对接口和方法进行统计。
调用的interface_method_needs_itable_index()函数的实现如下:
inline bool interface_method_needs_itable_index(Method* m) {
if (m->is_static())
return false; // e.g., Stream.empty
if (m->is_initializer())
return false; // <init> or <clinit>
// If an interface redeclares a method from java.lang.Object,
// it should already have a vtable index, don't touch it.
// e.g., CharSequence.toString (from initialize_vtable)
// if (m->has_vtable_index())
return false; // NO!
return true;
}
接口默认也继承了Object类,所以也会继承来自Object的5个方法,不过这5个方法并不需要itableEntry,已经在vtable中有对应的vtableEntry,所以直接返回false即可。
在visit_all_interfaces()方法中会调用CountInterfacesClosure类的doit()方法,类及方法的实现如下:
class CountInterfacesClosure : public InterfaceVisiterClosure {
private:
int _nof_methods;
int _nof_interfaces;
public:
CountInterfacesClosure() { _nof_methods = 0; _nof_interfaces = 0; }
int nof_methods() const { return _nof_methods; }
int nof_interfaces() const { return _nof_interfaces; }
void doit(Klass* intf, int method_count) {
_nof_methods += method_count;
_nof_interfaces++;
}
};
可以看到,doit()方法只是对接口数量和方法进行了简单的统计并保存到了_nof_interfaces与_nof_methods属性中。 这样后续就可以调用calc_itable_size()函数计算itable需要占用的空间大小了,方法的实现如下:
// Helper methods
static int calc_itable_size(int num_interfaces, int num_methods) {
return (num_interfaces * itableOffsetEntry::size()) + (num_methods * itableMethodEntry::size());
}
可以清楚的看到对于itable大小的计算逻辑,也就是接口占用的内存大小加上方法占用的内存大小之和。不过在compute_itable_size()方法中调用此方法时,num_interfaces为类实现的所有接口总数加1,所以最后一个是空的,这也是做为遍历接口的终止条件而存在。
计算好itable需要占用的内存大小后就可以部分初始化这个表中的一些信息了,接下来在parseClassFile()函数中有如下的调用语句:
// Initialize itable offset tables
klassItable::setup_itable_offset_table(this_klass);
调用的setup_itable_offset_table()函数的实现如下:
// Fill out offset table and interface klasses into the itable space
void klassItable::setup_itable_offset_table(instanceKlassHandle klass) {
if (klass->itable_length() == 0){
return;
} // Count no of interfaces and total number of interface methods
CountInterfacesClosure cic;
visit_all_interfaces(klass->transitive_interfaces(), &cic);
int nof_methods = cic.nof_methods();
int nof_interfaces = cic.nof_interfaces(); // Add one extra entry so we can null-terminate the table
nof_interfaces++; // Fill-out offset table
itableOffsetEntry* ioe = (itableOffsetEntry*)klass->start_of_itable();
itableMethodEntry* ime = (itableMethodEntry*)(ioe + nof_interfaces);
intptr_t* end = klass->end_of_itable(); // Visit all interfaces and initialize itable offset table
SetupItableClosure sic((address)klass(), ioe, ime);
visit_all_interfaces(klass->transitive_interfaces(), &sic); }
每一次调用visit_all_interfaces()方法计算出接口总数和需要itableEntry的方法总数,第二次调用visit_all_interfaces()去初始化itable中的相关信息,也就是在visit_all_interfaces()中调用doit()方法,不过这次调用的是SetupItableClosure类中定义的doit()方法。SetupItableClosure类的定义如下:
class SetupItableClosure : public InterfaceVisiterClosure {
private:
itableOffsetEntry* _offset_entry; // Klass* _interface; int _offset;
itableMethodEntry* _method_entry; // Method* _method
address _klass_begin;
public:
SetupItableClosure(address klass_begin, itableOffsetEntry* offset_entry, itableMethodEntry* method_entry) {
_klass_begin = klass_begin;
_offset_entry = offset_entry;
_method_entry = method_entry;
}
itableMethodEntry* method_entry() const { return _method_entry; }
void doit(Klass* intf, int method_count) {
int offset = ((address)_method_entry) - _klass_begin;
_offset_entry->initialize(intf, offset); // 初始化itableOffsetentry中的相关属性
_offset_entry++; // 指向下一个itableOffsetEntry
_method_entry += method_count; // 指向下一个接口中存储方法的itableMethodEntry
}
};
对itableOffsetEntry中的_interface与_interface初始化,在之前已经介绍过itableOffsetEntry类及相关属性,这里不再介绍。
相关文章的链接如下:
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的大小
作者持续维护的个人博客 classloading.com。
关注公众号,有HotSpot源码剖析系列文章!
计算itable的大小的更多相关文章
- 计算vtable的大小
在ClassFileParser::parseClassFile()函数中会计算vtable和itable所需要的大小,因为vtable和itable是内嵌在Klass中的,parseClassFil ...
- ios UITextView 计算文字内容大小
先设置好 textView的内容文字,再调用以下代码,就能够得到文字内容的size,其中参数表示最大的size的尺寸,通常,高度应该不限制,宽度是控件的宽度. let newSize = statem ...
- 【转】Android绘制View的过程研究——计算View的大小
Android绘制View的过程研究——计算View的大小 转自:http://liujianqiao398.blog.163.com/blog/static/18182725720121023218 ...
- iOS计算缓存文件的大小
//获取缓存文件路径 -(NSString *)getCachesPath{ // 获取Caches目录路径 NSArray *paths = NSSearchPathForDirectoriesIn ...
- PHP计算某个目录大小的方法
用PHP来计算某个目录大小的方法. PHP CURL session COOKIE 可以调用系统命令,还可以这样: <?php function dirsize($dir) { @$dh ...
- python计算文件夹大小(linux du命令 简化版)
C盘又满了,怎么办?用了一些垃圾清理软件(或者bat脚本),但是还是不理想,那么具体哪些文件夹下面有巨大的文件呢?windows并不能通过详细信息看到每个文件夹的大小(PS:这里所谓的文件夹的大小是指 ...
- Python_计算文件夹大小
计算文件夹大小 os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 os.path.join(path1[, path2[, ...]]) 将 ...
- ios 拉伸图片和计算文字的大小
一.拉伸图片 /** * 传入图片的名称,返回一张可拉伸不变形的图片 * * @param imageName 图片名称 * * @return 可拉伸图片 */ + (UIImage *)resiz ...
- Java中计算对象的大小
一.计算对象大小的方法 Java中如何计算对象的大小呢,找到了4种方法: 1.java.lang.instrument.Instrumentation的getObjectSize方法: 2.BTrac ...
随机推荐
- PyQt5绘图
QPainter 功能:QPainter实现在QWidget上画图功能 说明:绘图必须在paintEvent中完成,且要在bengin和end之间作图 接口: 方法 描述 begin 开始画图 end ...
- python读取hdfs上的parquet文件方式
在使用python做大数据和机器学习处理过程中,首先需要读取hdfs数据,对于常用格式数据一般比较容易读取,parquet略微特殊.从hdfs上使用python获取parquet格式数据的方法(当然也 ...
- .Net Core 常见错误解决记录
Error: String or binary data would be truncated. The statement has been terminated 数据库出错原因: 表字段创建的太短 ...
- 关于Type-C扩展坞干扰路由器交换机的解决方案
近期看到网友反馈Type-C扩展坞干扰交换机的问题,具体表现为USB Type-C扩展坞在同时插上网线和PD充电的情况下,引起路由器或交换机死机,导致局域网断开的情况.经实测分析,原因为部分电脑默认设 ...
- for循环实现Fibonacci数列
Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n. 输出格 ...
- Python os.fpathconf() 方法
概述 os.fpathconf() 方法用于返回一个打开的文件的系统配置信息.高佣联盟 www.cgewang.com Unix上可用. 语法 fpathconf()方法语法格式如下: os.fpat ...
- PHP array_search() 函数
实例 在数组中搜索键值 "red",并返回它的键名: <?php$a=array("a"=>"red","b" ...
- luogu P4632 [APIO2018] New Home 新家 线段树 set 二分
写了一种比较容易理解 但是常数很大的sol. 容易发现可以扫描线. 维护好序列之后发现很难查距离 考虑二分. 这里二分可以在线段树上进行 当然可能存在一些问题 如果离散化的话需要处理一些比较麻烦的细节 ...
- Spring学习总结(8)-接口多个实现类的动态调用
需求描述:当一个接口有2个以上的实现类时,调用方需要根据参数选择只其中一个实现类 Spring版本:5.1.8.RELEASE 1. 接口和实现类 /** * 接口 */ public interfa ...
- 基于asp.net core 从零搭建自己的业务框架(二)
前言 对于项目的迭代,如何降低复杂性的要求高于性能以及技术细节的 一个易用的项目,才能迭代到比拼性能,最后拼刺刀的阶段 传统单体项目,都是传统三层,直接请求响应的模式,这类称为Rpc模式,易用性上非常 ...