-
2020-07-13 15:39:00
- 在面向对象的编程中,会频繁的使用动态分派,如果在每次的动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话,可能影响到执行效率。因此,为了提高性能,JVM采用在类的方法区建立一个虚方法表(virtual method table)(非虚方法不会出现在表中)来实现。使用索引表来代替查找。
- 每个类中都有一个虚方法表,表中存着各个方法的实际入口。
- 虚方法表什么时候创建?
虚方法表会在类加载的链接阶段被创建并开始初始化,类的变量初始值准备完成之后,JVM会把该类的方法表也初始化完毕。
更多相关内容 -
jvm原理(34)虚方法表与动态分派机制详解
2018-09-15 12:05:13如果子类继承了父类,但是某个父类的方法没有被子类重写,那么在子类的方法表里边该方法指向的是父类的方法的入口,子类并不会重新生成一个方法,然后让方法表去指向这个生成的,这样做是没有意义的。还有一点,如果...编写代码:
public class MyTest7 { public static void main(String[] args) { Animal animal = new Animal(); Animal dog = new Dog(); animal.test("hello"); dog.test(new Date()); } } class Animal{ public void test(String str){ System.out.println("animal str"); } public void test(Date date){ System.out.println("animal date"); } } class Dog extends Animal{ @Override public void test(Date date) { System.out.println("dog date"); } @Override public void test(String str) { System.out.println("dog str"); } }
运行结果:
animal str dog date
方法表:
针对方法调用动态分派的过程,虚拟机会在类的方法区建立一个虚拟方法表的数据结构(virtual method table,vtable),
针对于invokeinterface指令来说,虚拟机会建立一个叫做接口方法表的数据结构(interface method table,itable)方法表会在类的连接阶段初始化,方法表存储的是该类方法入口的一个映射,比如父类的方法A的索引号是1,方法B的索引号是2。。。
如果子类继承了父类,但是某个父类的方法没有被子类重写,那么在子类的方法表里边该方法指向的是父类的方法的入口,子类并不会重新生成一个方法,然后让方法表去指向这个生成的,这样做是没有意义的。还有一点,如果子类重写了父类的方法,那么子类这个被重写的方法的索引和父类的该方法的索引是一致。比如父类
A的test方法被子类C重写了,那么子类C的test方法的索引和父类A的test方法的索引都是1(打个比方),这样做的目的是为了快速查找,比如说在子类里边找不到一个方法索引为1的方法,那么jvm会直接去父类查找方法索引为1的方法,不需要重新在父类里边遍历。 -
啊!Java虚方法
2021-03-09 22:52:25什么是Java的虚方法呢,我们首先看看什么是虚函数虚函数百度百科的解释为:在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的[成员函数],用法格式为:virtual 函数返回类型 函数名(参数表) {[函数体]}...什么是Java的虚方法呢,我们首先看看什么是虚函数
虚函数
百度百科的解释为:
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的[成员函数],用法格式为:virtual 函数返回类型 函数名(参数表) {[函数体]};实现[多态性],通过指向派生类的基类[指针]或引用,访问派生类中同名覆盖成员函数。
从上面解释上我们抓住几个关于虚函数的关键字 基类、派生类、同名覆盖(重写),因此我们可以理解为虚函数其实就是描述我们子类重写的父类方法。
在虚函数声明定义这块,C++可以通过virtual关键字来进行直接声明,而在Java中,并没有提供我们关键字来声明虚函数,但是我们通过虚函数的定义,我们可以理解为被override的方法都是virtual的。
关于虚方法的调用
在Java语言中,class文件被会解释成机器码,而方法调用会被解释成具体的方法调用指令,大致可以以下五类指令:
指令
描述
invokestatic
调用静态方法
invokespecial
调用实例构造方法,私有方法和父类方法
invokevirtual
调用虚方法
invokeinterface
调用接口方法,在运行时再确定一个实现此接口的对象
invokedynamic
在运行时动态解析出调用点限定符所引用的方法之后,调用该方法;
注意:invokedynamic 指令是jdk1.7才加入的,但是在jdk1.7中并没有开始使用。在jdk1.8中才开始大量使用,主要就是我们大量用的 lambda 表达式。
我们接下来去字节码验证下invokevirtual指令,so! 我们来举个🌰
// 类声明文件如下:
public class VirtualDemo {
public static void main(String[] args) {
Parent obj = new Child();
obj.func();
}
}
class Parent{
public void func(){
System.out.println("parent func");
}
}
class Child extends Parent{
@Override
public void func() {
System.out.println("child func");
}
}
接下来我们来获取这个类的字节码:
// dos 命令如下
javac VirtualDemo.java
javap -verbose VirtualDemo
VirtualDemo部分字节码指令如下:
VirtualDemo部分字节码指令.png
解释:在字节码第4行,我们可以看到是一个invokespecial指令,代表调用的是调用子类Child的实例构造init方法。在第9行中,我们可以看到是一个invokevirtual指令,表示调用虚方法,并且表示这是父类Parent中的方法func(),但这只是编译时是这样表示,等字节码真正执行的时候会通过方法表去正确找到子类Child中func()方法。
参考链接:
-
虚方法调用在Java虚拟机中的实现方式?
2021-05-20 00:27:56虚方法调用包括 invokevirtual 指令和 invokeinterface 指令。 如果这两种指令所声明的目标方法被标记为 final,那么 Java 虚拟机会采用静态绑定。否则,Java 虚拟机将采用动态绑定,在运行过程中根据调用者的动态...写在前面
本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和文献引用请见100个问题搞定Java虚拟机
解答
虚方法调用包括 invokevirtual 指令和 invokeinterface 指令。 如果这两种指令所声明的目标方法被标记为 final,那么 Java 虚拟机会采用静态绑定。否则,Java 虚拟机将采用动态绑定,在运行过程中根据调用者的动态类型,来决定具体的目标方法。 Java 虚拟机的动态绑定是通过方法表这一数据结构来实现的。 方法表中每一个重写方法的索引值,与父类方法表中被重写的方法的索引值一致。 在解析虚方法调用时,Java 虚拟机会纪录下所声明的目标方法的索引值,并且在运行过程中根据这个索引值查找具体的目标方法。 Java 虚拟机中的即时编译器会使用内联缓存来加速动态绑定。
补充
虚方法调用
Java 里所有非私有实例方法调用都会被编译成 invokevirtual 指令,而接口方法调用都会被编译成 invokeinterface 指令。
这两种指令,均属于 Java 虚拟机中的虚方法调用。
在绝大多数情况下,Java 虚拟机需要根据调用者的动态类型,来确定虚方法调用的目标方法。这个过程我们称之为动态绑定。
那么,相对于静态绑定的非虚方法调用来说,虚方法调用更加耗时。
final虚方法
在 Java 虚拟机中,静态绑定包括用于调用静态方法的 invokestatic 指令,和用于调用构造器、私有实例方法以及超类非私有实例方法的 invokespecial 指令。
如果虚方法调用指向一个标记为 final 的方法,那么 Java 虚拟机也可以静态绑定该虚方法调用的目标方法。
方法表
Java 虚拟机中采取了一种用空间换取时间的策略来实现动态绑定。它为每个类生成一张方法表,用以快速定位目标方法。
那么方法表具体是怎样实现的呢?
在类加载的准备阶段,除了为静态字段分配内存之外,还会构造与该类相关联的方法表。这个数据结构,便是 Java 虚拟机实现动态绑定的关键所在。
方法表本质上是一个数组,每个数组元素指向一个当前类及其祖先类中非私有的实例方法。
这些方法可能是具体的、可执行的方法,也可能是没有相应字节码的抽象方法。
方法表满足两个特质:
- 子类方法表中包含父类方法表中的所有方法;
- 子类方法在方法表中的索引值,与它所重写的父类方法的索引值相同。
方法调用指令中的符号引用会在执行之前解析成实际引用。
对于静态绑定的方法调用而言,实际引用将指向具体的目标方法。对于动态绑定的方法调用而言,实际引用则是方法表的索引值(实际上并不仅是索引值)。
在执行过程中,Java 虚拟机将获取调用者的实际类型,并在该实际类型的虚方法表中,根据索引值获得目标方法。这个过程便是动态绑定。
-
静态方法在Java中是一种非虚方法
2021-01-21 17:14:34静态的方法是非虚方法(Java中的非虚方法有private,final,static,构造器,非虚方法无需根据具体的对象遍历方法区的方法表,决定调用关系) 也是说,对于静态类型方法的调用,是其声明类型的,如Parent c = ... -
C++虚函数表解析
2021-03-03 03:42:59关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家一个清晰的剖析。当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是... -
编译原理实验之用C模拟C++虚方法表机制
2013-11-27 12:41:42实验五 C++虚方法表机制 一、实验目的 1.了解面向对象语言的单一继承的翻译 2.加深对面象对象语言中的继承和多态机制的理解 3.理解C++的虚方法表 二、实验说明 本实验通过对C++虚函数表机制的分析和模拟,进一步... -
Java多态(动态绑定)的底层原理:虚函数表
2021-02-07 15:36:44java多态的实现是通过itable(interface method table:接口方法表), vtable(virtual method table:虚方法表)来实现方法的准确跳转。 接口方法表和虚方法表的原理和C++的虚函数表类似。 C++的虚函数和纯函数的... -
虚函数表详解
2018-06-22 17:29:10本文转自:...这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。二、类的虚表每个包含了虚函数的类都包含一个虚表。我们知道,当一个类(A)继承另一个类(B)时... -
C++虚函数表详解
2019-01-05 09:51:57C++的虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖(override)的问题,保证其能真实的反应实际的函数。... -
【踩坑记录】虚函数表是什么时候生成的,虚函数表地址是什么时候给对象的;虚表指针是在构造函数之前就给了...
2021-07-25 20:55:47文章目录一、虚函数表二、首先讲一下结论:三、证明 一、虚函数表 虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址。 二、首先讲一下结论: 首先,虚函数表在编译时候就已经确定; 对象在构造前... -
C++虚函数表(vtable)和虚函数指针(vfptr)
2018-01-23 21:54:39编译器会构建一张虚表( vtable ),每一个类都有自己独特的虚表。同时,在这个继承链上,编译器会为基类插入一个隐式的指针(一般是对象的首地址),指向虚表,称为__vptr。然后,子类继承父类时,会获得继承下来的_... -
虚方法实现多态
2016-05-26 20:56:15大家都知道面向对象的三大特征,...这里的“相同”打上双引号是因为这里的相同的方法仅仅是看上去相同的方法,实际上它们调用的方法是不同的。 ——里氏转换多态是基于对象继承和里氏转换的,那么什么叫叫作里氏转换呢 -
C++虚函数及虚函数表简析
2021-01-20 07:11:35关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖析。 当然,相同的文章在网上也出现过一些了,但我总感觉这些文章... -
java方法的虚分派和方法表
2018-12-20 00:56:00java:方法的虚分派(virtual dispatch)和方法表(method table) Java方法调用的虚分派 虚分配(Virtual Dispatch) 首先从字节码中对方法的调用说起。Java的bytecode中方法的调用实现分为四种指令: ... -
C++一个类实例化后的对象中虚函数表是共用的吗
2019-06-17 09:15:08我们首先知道,不同的类具有不同的虚函数表,但是如果针对一个类如果实例化多个对象,那么这些对象的虚函数表是共用的吗?首先如果自己去设计内存占用时,从节省内存的角度去考虑的话,我觉得这些实例化后的类应该是... -
c++ 每个类都有一张虚方法表
2008-12-23 20:09:00#include using namespace std;/* 每个类都有一张虚方法表,当基类为虚方法,而派生类重载了虚方法,* 则虚方法表中的基类方法被派生类替换*/class AClass{ public: AClass(){ cout } virtual ~ -
C++虚函数表的工作原理.doc
2020-08-13 06:41:41虚函数表工作原理 C++中的虚函数的作用主要是实现了多态的机制关于多态简而言之就是用父类型别的指针指向其子类的实例然后通过父类的指针调用实际子类的成员函数这种技术可以让父类的指针有多种形态这是一种泛型技术... -
OC的方法都是虚方法--- 虚函数
2013-10-13 18:18:561、 父类的指针可以指向子类的对象。 但是调用方法时调用的子类的方法。 2。调用方法不看指针,看对象。 (好处:不同事物被相同事件触发,产生不同的结果) 3。 -
虚函数表结构
2019-07-15 18:45:41虚函数表 所谓虚函数表就是存放着当前类中所有虚函数地址的表。在实例化一个具有虚函数的类时,这个表也被分配到这个实例对象的内存中,通过虚函数表可以指明所要调用的函数的位置。在C++编译器中虚函数表的地址... -
多态(二、虚函数)
2022-02-07 20:46:06虚函数的数据储存在属于自己的虚函数表,通过虚函数指针指向。在类被继承的时候,虚函数表内的数据也会别继承,但是虚函数表不会被继承。 #include <iostream> #include <string> using namespace std... -
C#中虚方法(virtual)详解
2020-06-17 22:47:36虚方法如何定义? 虚方法怎么声明? 虚方法的规则是什么? 虚方法如何进行调用? 虚方法的作用是什么? 虚方法有什么好处? 虚方法使用时注意什么? -
简述虚函数表
2019-04-04 18:00:28据百度百科描述,C++并未规定用何种方法实现虚函数,但是大部分编译器厂商都选择使用虚函数表这种方法,那到底什么是虚函数表??? 虚函数表,称为V-Table。虚函数表是一片连续的内存区域,每个内存单元存放着... -
虚函数表存储位置
2019-06-17 13:59:30原 虚函数表放在哪里? 2007年01月28日 14:09:00 houdy 阅读数 15329 ... -
C++ 虚函数详解(虚函数表、vfptr)——带虚函数表的内存分布图
2019-05-16 23:44:49所有的虚函数都必须有定义,因为编译器直到运行前也不知道到底要调用哪个版本的虚函数。 只有通过指针或引用调用虚函数才会发生动态绑定,因为只有这种情况,引用或指针的静态类型与对象本身的动态类型才会不同。 -
C++虚表结构详解
2018-12-14 00:06:03为了实现虚函数,C++使用动态绑定方式,称为虚表。 虚表是一个包含函数的查找表,该查找表用于动态绑定方式...,“虚方法表”,“分派表”。 虚表虽然用语言描述有点复杂,实际上非常简单。 首先,每个包含虚... -
c++中虚基类表和虚函数表的布局
2017-04-30 17:47:44对象的每个基类都有一个属于自己的虚函数表指针(vfptr)指向虚函数表(vftbl)的某一项,都有一个属于自己的虚基类表指针(vbptr)指向虚基类表(vbtbl)的某一项。 3. 虚函数表中按照对象继承的顺序排列对象的虚函数...