在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. 解决win10安装flask-mysqldb报错 Python2.7

    win10上安装的pycharm,在pycharm创建的py2.7虚拟环境中安装flask-sqlalchemy 执行pip install flask-mysqldb报错 error: Micros ...

  2. 部署一套完整的Kubernetes高可用集群(二进制,v1.18版)

    一.前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式: kubeadm Kubeadm是一个K8s部署工具,提供kubeadm ...

  3. vue 修改路由

    直接放代码: this.$router.push({ path: "/login" });

  4. 云原生时代高性能Java框架—Quarkus(二)

    --- *构建Quarkus本地镜像.容器化部署Quarkus项目* Quarkus系列博文 Quarkus&GraalVM介绍.创建并启动第一个项目 构建Quarkus本地镜像.容器化部署Q ...

  5. 2018年5月15日的sqlite安装和数据库记录

    sqlite数据库安装在d:\sqlite_files运行sqlite3查看数据表,命令,.tables 数据库文件 d:\sqlite_files\device.db create table de ...

  6. 想学Python不知道从哪里开始学?|百度网盘免费下载| 这本入门书了解下

    百度网盘免费下载:编程小白的第一本 Python 入门书 提取码:s0pc Python是什么 Python是一种计算机程序设计语言,由吉多·范罗苏姆创造,第一版发布于1991年,可以视之为一种改良的 ...

  7. 十进制转换为其他进制(不使用format)

    base = [str(x) for x in range(10)] + [chr(x) for x in range(ord('A'), ord('A') + 6)] # ['0', '1', '2 ...

  8. PHP is_link() 函数

    定义和用法 The is_link() 函数检查指定的文件是否是一个连接. 如果文件是一个连接,该函数返回 TRUE. 语法 is_link(file) 参数 描述 file 必需.规定要检查的文件. ...

  9. C++模板沉思录(上)

    花下猫语: 在我们读者群里,最近出现了比较多关于 C++ 的讨论,还兴起了一股学习 C++ 的风气.樱雨楼小姐姐对 C++ 的模板深有研究,系统地梳理成了一篇近 4 万字的文章!本文是上篇,分享给大家 ...

  10. 一本通 高手训练 1781 死亡之树 状态压缩dp

    LINK:死亡之树 关于去重 还是有讲究的. 题目求本质不同的 具有k个叶子节点的树的个数 不能上矩阵树. 点数很少容易想到装压dp 考虑如何刻画树的形状 发现一个维度做不了 所以. 设状态 f[i] ...