计算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 ...
随机推荐
- 在VS Code中编写IAR项目
在VS Code中编写IAR项目 首先按照网上的教程,下载C/C++插件,以及IAR Eebedded Workbench插件,安装完成重启VS Code. 项目目录下新建.vscode文件夹,并新建 ...
- netty 使用字典提升短文本的压缩效果
1 问题 术语:压缩率,compression ratio,压缩后的大小/压缩前的大小,越小说明压缩效果越好. 在使用netty的JdkZlibEncoder进行压缩时,发现了一个问题:它对于短文本( ...
- python基础--14大内置模块(上)
python的内置模块(重点掌握以下模块) 什么是模块 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类 ...
- Linux系统zabbix_agentd客户端安装与配置
标注:官网下载zabbix安装包(zabbix安装包里包含了zabbix_agentd客户端安装包,我们只选择zabbix_agentd客户端安装) zbbix官网下载地址: http://www. ...
- P1359租用游艇(dp+dfs)
好久真的是好久没有做dp的问题了(QWQ)(我有学过这玩意???) 诶,人生呐! 今天来一个动归- 顺便可以回顾一下dfs. 这个题我觉得审题也非常重要 小可爱dp: #include <bit ...
- PHP floor() 函数
实例 向下舍入为最接近的整数: <?phpecho(floor(0.60) . "<br>");echo(floor(0.40) . "<br&g ...
- PDO::exec
PDO::exec — 执行一条 SQL 语句,并返回受影响的行数(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 int PDO::exec ( str ...
- P2569 [SCOI2010]股票交易 dp 单调队列优化
LINK:股票交易 题目确实不算难 但是坑点挺多 关于初值的处理问题我就wa了两次. 所以来谢罪. 由于在手中的邮票的数量存在限制 且每次买入卖出也有限制. 必然要多开一维来存每天的邮票数量. 那么容 ...
- C/C++编程笔记:C语言开发经典游戏项目《五子棋》,内含源码
上一节给大家分享了扫雷游戏的源代码,本篇文章当然也不会让大家失望,专门针对C语言入门或者学习了部分知识之后的小伙伴来练手的游戏项目——<五子棋大战>,本期并不是使用的easyX,因为考虑到 ...
- luogu P3217 [HNOI2011]数矩形
LINK:数矩形 题意:给出n个点 求出一个最大的矩形. 矩形可以使斜着的.(不会告诉你样例我算了几年 这道题的一个潜规则 矩形面积都是整数 我也不知道为啥一定是整数 姑且是题目输出的要求吧. 所以用 ...