在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的源代码

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)

7、HotSpot的类模型(3)

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)

10、HotSpot的对象模型(6)

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器

14、类的双亲委派机制

15、核心类的预装载

16、Java主类的装载

17、触发类的装载

18、类文件介绍

19、文件流

20、解析Class文件

21、常量池解析(1)

22、常量池解析(2)

23、字段解析(1)

24、字段解析之伪共享(2)

25、字段解析(3)

26、字段解析之OopMapBlock(4)

27、方法解析之Method与ConstMethod介绍

28、方法解析

29、klassVtable与klassItable类的介绍

30、计算vtable的大小

作者持续维护的个人博客  classloading.com

关注公众号,有HotSpot源码剖析系列文章!

 

  

 

  

计算itable的大小的更多相关文章

  1. 计算vtable的大小

    在ClassFileParser::parseClassFile()函数中会计算vtable和itable所需要的大小,因为vtable和itable是内嵌在Klass中的,parseClassFil ...

  2. ios UITextView 计算文字内容大小

    先设置好 textView的内容文字,再调用以下代码,就能够得到文字内容的size,其中参数表示最大的size的尺寸,通常,高度应该不限制,宽度是控件的宽度. let newSize = statem ...

  3. 【转】Android绘制View的过程研究——计算View的大小

    Android绘制View的过程研究——计算View的大小 转自:http://liujianqiao398.blog.163.com/blog/static/18182725720121023218 ...

  4. iOS计算缓存文件的大小

    //获取缓存文件路径 -(NSString *)getCachesPath{ // 获取Caches目录路径 NSArray *paths = NSSearchPathForDirectoriesIn ...

  5. PHP计算某个目录大小的方法

    用PHP来计算某个目录大小的方法. PHP CURL session COOKIE  可以调用系统命令,还可以这样:  <?php function dirsize($dir) {  @$dh  ...

  6. python计算文件夹大小(linux du命令 简化版)

    C盘又满了,怎么办?用了一些垃圾清理软件(或者bat脚本),但是还是不理想,那么具体哪些文件夹下面有巨大的文件呢?windows并不能通过详细信息看到每个文件夹的大小(PS:这里所谓的文件夹的大小是指 ...

  7. Python_计算文件夹大小

    计算文件夹大小 os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 os.path.join(path1[, path2[, ...]]) 将 ...

  8. ios 拉伸图片和计算文字的大小

    一.拉伸图片 /** * 传入图片的名称,返回一张可拉伸不变形的图片 * * @param imageName 图片名称 * * @return 可拉伸图片 */ + (UIImage *)resiz ...

  9. Java中计算对象的大小

    一.计算对象大小的方法 Java中如何计算对象的大小呢,找到了4种方法: 1.java.lang.instrument.Instrumentation的getObjectSize方法: 2.BTrac ...

随机推荐

  1. PyQt5绘图

    QPainter 功能:QPainter实现在QWidget上画图功能 说明:绘图必须在paintEvent中完成,且要在bengin和end之间作图 接口: 方法 描述 begin 开始画图 end ...

  2. python读取hdfs上的parquet文件方式

    在使用python做大数据和机器学习处理过程中,首先需要读取hdfs数据,对于常用格式数据一般比较容易读取,parquet略微特殊.从hdfs上使用python获取parquet格式数据的方法(当然也 ...

  3. .Net Core 常见错误解决记录

    Error: String or binary data would be truncated. The statement has been terminated 数据库出错原因: 表字段创建的太短 ...

  4. 关于Type-C扩展坞干扰路由器交换机的解决方案

    近期看到网友反馈Type-C扩展坞干扰交换机的问题,具体表现为USB Type-C扩展坞在同时插上网线和PD充电的情况下,引起路由器或交换机死机,导致局域网断开的情况.经实测分析,原因为部分电脑默认设 ...

  5. for循环实现Fibonacci数列

    Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n. 输出格 ...

  6. Python os.fpathconf() 方法

    概述 os.fpathconf() 方法用于返回一个打开的文件的系统配置信息.高佣联盟 www.cgewang.com Unix上可用. 语法 fpathconf()方法语法格式如下: os.fpat ...

  7. PHP array_search() 函数

    实例 在数组中搜索键值 "red",并返回它的键名: <?php$a=array("a"=>"red","b" ...

  8. luogu P4632 [APIO2018] New Home 新家 线段树 set 二分

    写了一种比较容易理解 但是常数很大的sol. 容易发现可以扫描线. 维护好序列之后发现很难查距离 考虑二分. 这里二分可以在线段树上进行 当然可能存在一些问题 如果离散化的话需要处理一些比较麻烦的细节 ...

  9. Spring学习总结(8)-接口多个实现类的动态调用

    需求描述:当一个接口有2个以上的实现类时,调用方需要根据参数选择只其中一个实现类 Spring版本:5.1.8.RELEASE 1. 接口和实现类 /** * 接口 */ public interfa ...

  10. 基于asp.net core 从零搭建自己的业务框架(二)

    前言 对于项目的迭代,如何降低复杂性的要求高于性能以及技术细节的 一个易用的项目,才能迭代到比拼性能,最后拼刺刀的阶段 传统单体项目,都是传统三层,直接请求响应的模式,这类称为Rpc模式,易用性上非常 ...