计算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 ...
随机推荐
- ssh配置、vscode使用及常用扩展
1.ssh配置 1.1 进入命令行 win + r > cmd 1.2 输入如下代码直接回车即可生成ssh ssh-keygen -t rsa -C "xxx@qq.com&quo ...
- 详解TCP一:三次握手、四次挥手
TCP协议同样是运输层的协议,掌握TCP重点要关注这几个问题:顺序问题.丢包问题.连接维护.流量控制.拥塞控制.先解析下TCP报文段结构,相比于UDP要复杂很多. 首先还是两个端口号,对应着具体的应用 ...
- idea 自动生成try/catch代码块的快捷键
好像每个人的快捷键可能不同:我的是 Alt+Shift+Z 网上查的是 Ctrl+Alt+T 如果都不是可以点选工具栏生成try/catch(并可查看到自己的快捷键是什么):Code->Su ...
- Ribbon负载均衡接口
IRule--负载均衡规则 1.RounRobinRule:轮询负载均衡,通过累加取余获取服务,默认规则 2.RandomRule:随机负载均衡 3.WeightedResponseTimeRule: ...
- 大汇总 | 一文学会八篇经典CNN论文
本文主要是回顾一下一些经典的CNN网络的主要贡献. 论文传送门 [google团队] [2014.09]inception v1: https://arxiv.org/pdf/1409.4842.pd ...
- 我自己总结的sqlite的命令行命令集
我自己总结的sqlite 的命令行命令 导入文本数据文件时,设置分隔符为","sql>.separator "," sql>import devic ...
- Visio的快速使用和功能理念
以前我对visio这种Microsoft办公套件并不感冒,觉得完全没必要用Visio作图,最多用一下Process on,其他基本用Xmind解决问题了.但是最近几次工作需要接触之后发现,Visio还 ...
- 【IDEA】HTML通过servlet3.0注解名提交表单到servlet类找不到页面的问题
IDEA一时爽,摸不着头的BUG火葬场 这个问题困扰我整整一天一夜,先是代码检查路径设置找不出问题,后面换tomcat版不行,抱着侥幸心理换IDEA版本意料之中还是没解决问题. 都快想秃了最后终于完美 ...
- numpy第三方库
# 导入numpy 并赋予别名 np import numpy as np # 创建数组的常用的几种方式(列表,元组,range,arange,linspace(创建的是等差数组),zeros(全为 ...
- PHP Cookie是什么
PHP Cookie cookie 常用于识别用户. Cookie 是什么? cookie 常用于识别用户.cookie 是一种服务器留在用户计算机上的小文件.每当同一台计算机通过浏览器请求页面时,这 ...