java:方法的虚分派(virtual dispatch)和方法表(method table)

Java方法调用的虚分派

虚分配(Virtual Dispatch)

首先从字节码中对方法的调用说起。Java的bytecode中方法的调用实现分为四种指令:

invokevirtual为最常见的情况,包含virtual dispatch机制;

invokerspecial是作为对private和构造方法的调用,绕过了virtual dispatch;

invokeinterface的实现跟invokevirtual类似;

invokestatic是对静态方法的调用;

其中最复杂的要属invokevirtual指令,它涉及到了多态的特性,使用virtual dispatch做方法调用。

virtual dispatch机制会首先从receiver(被调用方法的对象)的类的实现中查找对应的方法,如果没找到,则去父类查找,直找到函数并实现调用,而不是依赖于引用的类型。

下面是一段有趣的代码。反映了virtual dispatch机制和一般的field访问的不同。

运行结果:

前两行输出中,对于Intro这个属性的访问,直接指向了父类中的变量,因为引用类型为父类。

第二行对于target()的方法调用,则是指向了子类中的方法,虽然引用类型也为父类,但这是虚分配的结果,虚分配不管引用类型的,只查被调用对象的类型·。

既然需分派机制是从被调用对象本身的类开始查找,那么对于一个覆盖了父类中某方法的子类的对象,是无法调用父类中那个被覆盖的方法的吗?

在虚分派机制中这确实是不可以的。但却可以通过invokespecial实现。如下代码:

func()就成功地调用了父类的方法target(),虽然target()已经被子类重写了。具体的调用细节,从字节码中可以看到:

其中使用了invokespecial指令,而不是施行需分派策略的invokevirtual指令。

方法表(Method Table)

介绍了虚分派,接下来介绍是它的一种实现方法—方法表。类似于C++虚函数表vtbl。

在有的JVM实现中,使用了方法表机制实现虚分派,而有时候,为了节省内存可能不采用方法表的实现。

不要被方法表这个名字迷惑,它并不是记录所有方法的表。它是为虚分派服务,不会记录用invokestatic调用的静态方法和用invokespecial调用的够着函数和私有方法。

JVM会在链接类的过程中,给类分配相应的方法表内存空间。每个类对应一个方法表。这些都是存在于method area区中的。这里与C++略有不同,C++中每个对象的第一个指针就是指向了相应的虚函数表。而Java中每个对象索引到对应的类,在对应的类数据中对应一个方法表。

一种方法表的实现如下:

父类的方法比子类的方法先得到解析,即父类的方法相对于子类的方法位于表的前列。

表中每项对应于一个方法,索引到实际方法的实际代码上。如果子类重写了父类中某个方法的代码,则该方法第一次出现的位置的索引更换到子类的实现代码上,而不会在方法表中出现新的项。

JVM运行时,当代码索引到一个方法时,是根据它在方法表中的偏移量来实现访问。(第一次执行到调用指令时,会执行解释,将符号索引替换为对应的直接索引)。

invokeinterface与invokevirtual的比较

当使用invokeinterface来调用方法时,由于不同的类可以实现同一interface,我们无法确定在某个类中的interface中的方法处在哪个位置。于是,也就无法解释CONSTANT_interfaceMethodref-info为直接索引,而必须每次都执行一次在methodtable中的搜索了。所以,在这种实现中,通过invokeinterface访问方法比通过invokevirtual访问明显慢很多。

java方法的虚分派和方法表的更多相关文章

  1. 通过字节码分析Java方法的静态分派与动态分派机制

    在上一次[https://www.cnblogs.com/webor2006/p/9723289.html]中已经对Java方法的静态分派在字节码中的表现了,也就是方法重载其实是一种静态分派的体现,这 ...

  2. java内省机制及PropertyUtils使用方法

    背景 一般情况下,在Java中你可以通过get方法轻松获取beans中的属性值.但是,当你事先不知道beans的类型或者将要访问或修改的属性名时,该怎么办?Java语言中提供了一些像java.bean ...

  3. Java中的equals和hashCode方法

    本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...

  4. Java提高篇——equals()与hashCode()方法详解

    java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...

  5. Java中各种(类、方法、属性)访问修饰符与修饰符的说明

    类: 访问修饰符 修饰符 class 类名称 extends 父类名称 implement 接口名称 (访问修饰符与修饰符的位置可以互换) 访问修饰符 名称 说明 备注 public 可以被本项目的所 ...

  6. [原创]java WEB学习笔记79:Hibernate学习之路--- 四种对象的状态,session核心方法:save()方法,persist()方法,get() 和 load() 方法,update()方法,saveOrUpdate() 方法,merge() 方法,delete() 方法,evict(),hibernate 调用存储过程,hibernate 与 触发器协同工作

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  7. Java 重写hashCode 方法和equals方法

    package Container; import java.util.HashSet; import java.util.Iterator; /* Set 元素是无序的(存入和取出的顺序不一定一致) ...

  8. java中equals方法和hashcode方法的区别和联系,以及为什么要重写这两个方法,不重写会怎样

    一.在Object类中的定义为:public native int hashCode();是一个本地方法,返回的对象的地址值.但是,同样的思路,在String等封装类中对此方法进行了重写.方法调用得到 ...

  9. Java中的equals和hashCode方法详解

    Java中的equals和hashCode方法详解  转自 https://www.cnblogs.com/crazylqy/category/655181.html 参考:http://blog.c ...

随机推荐

  1. 使用Redis存储Nginx+Tomcat负载均衡集群的Session

    配置Tomcat的session共享可以有三种解决方案: 第一种是以负载均衡服务器本身提供的session共享策略,每种服务期的配置是不一样的并且nginx本身是没有的. 第二种是利用web容器本身的 ...

  2. [Qt Creator 快速入门] 第5章 应用程序主窗口

    对于日常见到的应用程序而言,许多都是基于主窗口的,主窗口中包含了菜单栏.工具栏.状态栏和中心区域等.这一章会详细介绍主窗口的每一个部分,还会涉及资源管理.富文本处理.拖放操作和文档打印等相关内容.重点 ...

  3. [Usaco2015 Jan]Moovie Mooving

    Description Bessie is out at the movies. Being mischievous as always, she has decided to hide from F ...

  4. 238 Product of Array Except Self 除自身以外数组的乘积

    一个长度为 n 的整形数组nums,其中 n > 1,返回一个数组 output ,其中 output[i] 等于nums中除nums[i]以外所有元素的乘积.不用除法 且在O(n)内解决这个问 ...

  5. [转].net cookie版购物车

    本文转自:http://www.sulong.cc/article/program/aspx/110613114249.html #region 添加到购物车AddShoppingCar /// &l ...

  6. 21 调试我们的C#程序

    我们写的C#程序,很多的时候不是能一次就写对的.尤其是在实际的项目中,你经常要做的工作就是不断修改你写的程序,让它正常运行.程序有错误,主要是两种情况.一种是程序报错了,不能正常运行下去.另一种是程序 ...

  7. 北大ACM(POJ1020-Anniversary Cake)

    Question:http://poj.org/problem?id=1020 问题点:DFS. Memory: 260K Time: 47MS Language: C++ Result: Accep ...

  8. VC++函数只被调用一次

    如何保证某个函数只被调用一次   一个函数caller会在其内部调用另外一个函数callee,现在的情况是,caller可能会在多个地方被多次调用,而你希望callee只在第一次被调用时被调用一次.一 ...

  9. jstree -- 使用JSON 数据组装成树

    概述: 前面主要是html数据,这里主要是json数组 1.格式 jsTree需要一个具体格式JSON数据,在标准的语法没有那个字段是必须的-而是那些是你需要的.请记住你可以获取任何你请求的其他属性, ...

  10. codeforces_459D_(线段树,离散化,求逆序数)

    链接:http://codeforces.com/problemset/problem/459/D D. Pashmak and Parmida's problem time limit per te ...