HotSpot二分模型(1)
HotSpot采用了OOP-Klass模型来描述Java类和对象。OOP(Ordinary Object Pointer)指的是普通对象指针,而Klass用来描述对象的具体类型。
那么为何要设计这样一个一分为二的对象模型呢?因为类和对象本来就不是一个概念,分别使用不同的对象模型描述符合软件开发的设计思想。另外英文注释也说明了其中的一个原因:
One reason for the oop/klass dichotomy in the implementation is that we don't want a C++ vtbl pointer in every object. Thus,
normal oops don't have any virtual functions. Instead, they forward all "virtual" functions to their klass, which does have
a vtbl and does the C++ dispatch depending on the object's actual type. (See oop.inline.hpp for some of the forwarding code.)
根据注释描述,HotSopt的设计者不想让每个对象中都含有一个vtable(虚函数表),所以就把对象模型拆成klass和oop,其中oop中不含有任何虚函数,而klass就含有虚函数表,可以进行方法分发。
我们简单介绍一下C++中对象的内存布局,这样才能了解二分模型设计的原因。同时也要介绍一下关于C++中虚函数的分派,这样在讲解Java语言的多态时就不用再补这一块的C++知识了。
下面分情况介绍C++对象的内存局部。
1、只含有数据成员的对象
class Base1{ public:
int base1_var1;
int base1_var2; };
通过在VS中配置/d1 reportSingleClassLayoutBase1命令来查看对象的内存布局,如下:
1> class Base1 size(8):
1> +---
1> 0 | base1_var1
1> 4 | base1_var2
1> +---
可以看到,成员变量是按照定义的顺序来保存的,类对象的大小就是所有成员变量的大小之和。
2、没有虚函数的对象
class Base1{ public:
int base1_var1;
int base1_var2; void func(){}
};
C++中有方法的动态分派,就类似于Java中方法的多态。而C++实现动态分派主要就是通过虚函数来完成的,非虚函数在编译时就已经确定调用目标。C++中的虚函数通过关键字virtual来声明,如上函数func()没有virtual关键字,所以是非虚函数。
查看内存布局,如下:
1> class Base1 size(8):
1> +---
1> 0 | base1_var1
1> 4 | base1_var2
1> +---
非虚函数不会影响内存布局。
3、含有虚函数的对象
class Base1{ public:
int base1_var1;
int base1_var2; virtual void base1_fun1() {} };
内存布局如下:
1> class Base1 size(16):
1> +---
1> 0 | {vfptr}
1> 8 | base1_var1
1> 12 | base1_var2
1> +---
在64位环境下,指针占用8字节,而vfptr就是指向虚函数表(vtable)的指针,其类型为void**, 这说明它是一个void*指针。类似于在类Base1中定义了如下类似的伪代码:
void* vtable[1] = { &Base1::base1_fun1 }; const void** vfptr = &vtable[0];
另外我们还可以看到,虚函数指针vfptr位于所有的成员变量之前。
我们在上面的例子中再添加一个虚函数,如下:
virtual void base1_fun2() {}
内存布局如下:
1> class Base1 size(16):
1> +---
1> 0 | {vfptr}
1> 8 | base1_var1
1> 12 | base1_var2
1> +---
可以看到,内存布局无论有一个还是多个虚函数都是一样的,改变的只是vfptr指向的虚函数表中的项。类似于在类Base1中定义了如下类似的伪代码:
void* vtable[] = { &Base1::base1_fun1, &Base1::base1_fun2 }; const void** vfptr = &vtable[0];
4、继承类对象
class Base1{ public: int base1_var1;
int base1_var2; virtual void base1_fun1() {}
virtual void base1_fun2() {} }; class Derive1 : public Base1{ public: int derive1_var1;
int derive1_var2; };
通过在VS中配置/d1 reportSingleClassLayoutDerive1命令来查看Derive1对象的内存布局,如下:
1> class Derive1 size(24):
1> +---
1> | +--- (base class Base1)
1> 0 | | {vfptr}
1> 8 | | base1_var1
1> 12 | | base1_var2
1> | +---
1> 16 | derive1_var1
1> 20 | derive1_var2
1> +---
可以看到,基类在上边, 继承类的成员在下边,并且基类的内存布局与之前介绍的一模一样。继续来改造如上的实例,为派生类Derive1添加一个与基本base1_fun1()函数一模一样的虚函数,如下:
class Base1{ public: int base1_var1;
int base1_var2; virtual void base1_fun1() {}
virtual void base1_fun2() {} }; class Derive1 : public Base1{ public: int derive1_var1;
int derive1_var2; virtual void base1_fun1() {} // 覆盖基类函数 };
布局如下:
1> class Derive1 size(24):
1> +---
1> | +--- (base class Base1)
1> 0 | | {vfptr}
1> 8 | | base1_var1
1> 12 | | base1_var2
1> | +---
1> 16 | derive1_var1
1> 20 | derive1_var2
1> +---
基本的布局没变,不过由于发生了虚函数覆盖,所以虚函数表中的内容已经发生了变化,类似于在类Derive1中定义了如下类似的伪代码:
void* vtable[] = { &Derive1::base1_fun1, &Base1::base1_fun2 }; const void** vfptr = &vtable[0];
可以看到,vtable[0]指针指向的是Derive1::base1_fun1()函数。所以当调用Derive1对象的base1_fun1()函数时,会根据虚函数表找到Derive1::base1_fun1()函数进行调用,而当调用Base1对象的base1_fun1()函数时,由于Base1对象的虚函数表中的vtable[0]指针指向Base1::base1_func1()函数,所以会调用Base1::base1_fun1()函数。是不是和Java中方法的多态很像?那么HotSpot虚拟机是怎么实现Java方法的多态呢?我们后续在讲解Java方法时会详细介绍。
下面继续看虚函数的相关实例,如下:
class Base1{ public: int base1_var1;
int base1_var2; virtual void base1_fun1() {}
virtual void base1_fun2() {} }; class Derive1 : public Base1{ public: int derive1_var1;
int derive1_var2; virtual void derive1_fun1() {} };
对象的内存布局如下:
1> class Derive1 size(24):
1> +---
1> | +--- (base class Base1)
1> 0 | | {vfptr}
1> 8 | | base1_var1
1> 12 | | base1_var2
1> | +---
1> 16 | derive1_var1
1> 20 | derive1_var2
1> +---
对象的内存布局没有改变,改变的仍然是虚函数表,类似于在类Derive1中定义了如下类似的伪代码:
void* vtable[] = { &Derive1::base1_fun1, &Base1::base1_fun2,&Derive1::derive1_fun1 }; const void** vfptr = &vtable[0];
可以看到,在虚函数表中追加了&Derive1::derive1_fun1()函数。
好了,关于对象的布局我们就简单的介绍到这里,因为毕竟不是在研究C++,只要够我们研究HotSpot时使用就够了,更多关于内存布局的知识请参考其它文章或书籍。
HotSpot二分模型(1)的更多相关文章
- HotSpot类模型之ArrayKlass
上一篇分析了 HotSpot类模型之InstanceKlass ,这次主要分析表示java数组类型的C++类. 1.ArrayKlass类 ArrayKlass继承自Klass,是所有数组类的抽象基类 ...
- HotSpot类模型之InstanceKlass
上一篇 HotSpot源码分析之类模型 介绍了类模型的基础类Klass的重要属性及方法,这一篇介绍一下InstanceKlass及InstanceKlass的子类. 1.InstanceKlass类 ...
- HotSpot的类模型(2)
在前一篇文章 HotSpot的二分模型中已经讲过,HotSpot采用了OOP-Klass模型描述Java的类和对象.Klass模型采用Klass类及相关子类来表示具体的Java类,可以理解这些类为Ja ...
- HotSpot的类模型(3)
上一篇 HotSpot的类模型(2) 介绍了类模型的基础类Klass的重要属性及方法,这一篇介绍一下InstanceKlass及InstanceKlass的子类. 2.InstanceKlass类 每 ...
- HotSpot的类模型(4)
我们继续接着上一篇 HotSpot的类模型(3)分析,这次主要分析表示java数组的C++类. 4.ArrayKlass类 ArrayKlass继承自Klass,是所有数组类的抽象基类,类及重要属性的 ...
- HotSpot的对象模型(6)
接着上一篇,我们继续来讲oopDesc相关的子类. 3.instanceOopDesc类 instanceOopDesc类的实例表示除数组对象外的其它对象.在HotSpot中,对象在内存中存储的布局可 ...
- HotSpot的执行引擎-CallStub栈帧
之前多次提到接触到调用JavaCalls::call()方法来执行Java方法,如: (1)Java主类装载时,调用JavaCalls::call()方法执行的Java方法checkAndLoadMa ...
- 操作句柄Handle(7)
可以将Handle理解成访问对象的一个“句柄”.垃圾回收时对象可能被移动(对象地址发生改变),通过Handle访问对象可以对使用者屏蔽垃圾回收细节. Handle涉及到的相关类的继承关系如下图所示. ...
- 句柄Handle的释放(8)
本篇首先介绍几个与句柄分配与释放密切相关的类,然后重点介绍句柄的释放. 1.HandleArea.Area与Chunk 句柄都是在HandleArea中分配并存储的,类的定义如下: // Thread ...
随机推荐
- CSDN账号被冻结了怎么办
CSDN可能因为你的博客里有一些网站链接给你判断为恶意推广广告,冻结, 或者和我一样,在评论区刷屏被冻结, 联系客服即可,向客服提供你的绑定邮箱或绑定手机号,或博客id,客服会给你解冻 PS: 找不到 ...
- Java实现图形化计算器
package java计算器; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionL ...
- /etc/alternatives
如何安装一个可执行程序 一般来说我们一个可执行程序,可能在多个路径下,比如在opt路径下,或者在自己的home下. 当要达到在系统的任意路径下敲击该命令,都可执行的话,一般要将该可执行命令的路径加入到 ...
- 百度poi搜索
package baidumapsdk.demo.search; import android.os.Bundle; import android.support.v4.app.FragmentAct ...
- HDU - 2546 饭卡 题解
题目大意 电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够) ...
- 2.使用nexus3配置docker私有仓库
1,配置走起 1,创建blob存储 登陆之后,先创建一个用于存储镜像的空间. 定义一个name,下边的内容会自动补全. 然后保存. 注意:实际生产中使用,建议服务器存储500G或以上. 2,创建一个h ...
- 分享我在前后端分离项目中Gitlab-CI的经验
长话短说,今天分享我为前后端分离项目搭建Gitlab CI/CD流程的一些额外经验. Before Gitlab-ci是Gitlab提供的CI/CD特性,结合Gitlab简单友好的配置界面,能愉悦的在 ...
- Spark原始码系列(六)Shuffle的过程解析
问题导读: 1.shuffle过程的划分? 2.shuffle的中间结果如何存储? 3.shuffle的数据如何拉取过来? Shuffle过程的划分 Spark的操作模型是基于RDD的,当调用RD ...
- TypeError: this.xxx.substring is not a function的解决办法
这是因为已经改变了xxx的值的类型,不再是字符串的话将不会拥有substring函数, 我当时这样写的时候,直接将number类型赋予了this.enter,所以导致了错误. 改为这样之后可以使用su ...
- Jmeter接口测试,往MySQL数据库写数据时,中文显示???
调Jmeter接口测试,请求字段输入中文,查看数据库插入情况, 发现数据库显示 ???