`
javatgo
  • 浏览: 1169339 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

Virtual Dispatch

首先从字节码中对方法的调用说起。

java的bytecode中对方法的调用实现分为四种情况:

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

2.invokespecial是作为private和构造方法的调用,绕过了virtual dispatch;

3.invokeinterface的实现跟invokevirtual类似。

4.invokestatic是对静态方法的调用。

 

其中最复杂的要属 invokevirtual .

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

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

 

  1. public class Greeting {  
  2.     String intro = "Hello";  
  3.     String target(){  
  4.         return "world";  
  5.     }  
  6. }  

  1. public class FrenchGreeting extends Greeting {  
  2.     String intro = "Bonjour";  
  3.     String target(){  
  4.         return "le monde";  
  5.     }  
  6.       
  7.       
  8.     public static void main(String[] args){  
  9.         Greeting english = new Greeting();  
  10.         Greeting french = new FrenchGreeting();  
  11.           
  12.         System.out.println(english.intro + "," + english.target());  
  13.         System.out.println(french.intro + "," + french.target());  
  14.         System.out.println(((FrenchGreeting)french).intro + "," + ((FrenchGreeting)french).target());  
  15.     }  
  16. }  


运行的结果为

 

 

  1. Hello,world  
  2. Hello,le monde  
  3. Bonjour,le monde  


其中的第二行是亮点。

 

 对于intro这个filed的访问,直接指向了父类中的变量,因为引用 类型为父类。

而对于target的方法调用,则是指向了子类中的方法,虽然引用类型也为父类,但这是virtual dispatch的结果,virtual dispatch不管引用类型的,只查receiver的类型。


既然 虚分派 机制是从receiver对象的子类开始查找,由此看来,对于一个覆盖了父类中某方法的子类的对象,是无法调用父类中那个被覆盖的方法的吗?

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

 

  1. public class FrenchGreeting extends Greeting {  
  2.     String intro = "Bonjour";  
  3.     String target(){  
  4.         return "le monde";  
  5.     }  
  6.       
  7.     public String func(){  
  8.         return super.target();  
  9.     }  
  10.       
  11.       
  12.     public static void main(String[] args){  
  13.         Greeting english = new Greeting();  
  14.         FrenchGreeting french = new FrenchGreeting();  
  15.           
  16.         System.out.println(french.func());  
  17.           
  18.     }  
  19. }  


func()就成功的调用了父类的方法target,虽然target已经被子类重写了。怎么实现的?让我们看一看func方法中生成的字节码:

 

 

  1. ALOAD 0: this  
  2. INVOKESPECIAL Greeting.target() : String  
  3. ARETURN  

原来如此,它是通过invokespecial 指令来调用的。

 

 

Method Table

 

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

在有的JVM实现中,使用了方法表(method table)机制实现虚分派(virtual dispatch)。(有时候,为了节省内存而不采用method table实现)

不要被method table这个名字迷惑,它显然不是记录所有method的表。它是为virtual dispatch服务,所以,排除了用invokestatic调用的静态方法和用invokespecial调用的构造函数和私有方法。

JVM会在链接类的过程中,给类分配相应的method table内存空间。每个类对应一个方法表。这些都是存在于method area区中的。这里与C++略有不同,C++中每个对象的第一个指针就是指向了相应的虚函数表。而java中每个对象索引到对应的类,在对应的类数据中对应一个方法表。(关于链接的详细信息,参见博文《java类的装载(Loading)、链接(Linking)和初始化(Initialization)》)


根据《Inside the Java2 Virtual Machine》中所描述,一种方法表的实现如下:

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

JVM运行时,当代码索引到一个方法时,是根据它在方法表中的偏移量来实现访问的。(第一次执行到调用指令时,会将符号索引替换为对应的直接索引,执行解析)。由于invokevirtual调用的方法,在对应的类的method table中都有固定的位置,直接索引的值可以用偏移量来表示。(符号索引解析的最终目的是完成直接索引:对象方法和对象变量的调用都是用偏移量来表示直接索引的)

当使用invokeinterface来调用方法时,则没有那么幸运了。由于不同的类可以实现同一interface,我们无法确定在某个类中的inteface中的方法处在哪个位置。于是,也就无法解析 CONSTANT_intefaceMethodref-info为直接索引,而必须每次都执行一次在methodtable中的搜索了!

所以,在这种实现中通过 invokeinterface访问方法比通过invokevirtual访问明显慢!

0
3
分享到:
评论

相关推荐

    一个Delphi分派程序演示

    1. **虚拟方法表(Virtual Method Table, VMT)** Delphi使用虚拟方法表来实现动态分派。每个类都有一个VMT,其中存储了类及其所有父类的虚方法指针。当调用虚方法时,编译器会生成代码来通过对象的VMT找到正确的...

    Java模拟双分派DoubleDispatch

    分派/dispatch是指如何给一个消息绑定其方法体。Java、C#等仅仅支持单分派(singledispatch)而不支持双分派(double dispatch)。【相关概念,参考《设计模式.5.11访问者模式》p223】对于消息a.foo(b),假设有父类X...

    C++虚表,你搞懂了吗?

    虚表是一种利用程序语言实现的dynamic dispatch机制,或者说runtime method binding机制,也是我们说的多态。  注:笔者在本文使用C++语言,并且统一用vTable来表示虚表。  虚函数  用virtual关键字修饰的函数...

    connectify dispatch 4.0完美破解版

    connectify dispatch 4.0完美破解版

    Connectify Dispatch Hotspot Pro

    注册汉化方法: 双击“Installer”安装...复制Crack目录中的dispatch.dll和web文件夹到软件安装目录中的\plugins\dispatch目录覆盖同名文件。 复制Crack目录中的connectify.exe文件到安装程序目录覆盖同名文件。

    Dispatch IDS for IExplorer Dispatch Events

    Dispatch IDS for IExplorer Dispatch Events

    深入理解Java虚拟机笔记(带目录).docx

    深入理解 Java 虚拟机笔记 ...Java 中的虚方法表用于存储虚方法的信息,包括方法的名称、描述符和方法体。 Java 内存模型(JMM) Java 内存模型(JMM)是 Java 语言的内存模型,用于定义 Java 程序的内存行为。

    Swift-dispatch-semaphore

    Swift中的`DispatchSemaphore`是GCD(Grand Central Dispatch)的一部分,它是Apple的多线程和并发解决方案。在iOS开发中,理解并熟练使用`DispatchSemaphore`对于优化代码性能和处理同步问题至关重要。本文将深入...

    DISPATCH

    在网页设计中,CSS(层叠样式表)允许开发者控制文本的字体、大小、颜色和其他样式属性,实现跨浏览器的一致性。同时,随着Web字体服务的兴起,设计师现在可以在网页上使用各种高质量的网络字体,而无需担心用户设备...

    dispatch_barrier_(a)sync

    异步和同步的栅栏函数都有以下特点: 1、通过dispatch_barrier_(a)sync添加的block会等待前边所有的block执行完(不包括回调)才执行。 2、在其后添加的block会在dispatch_barrier_(a)sync添加的block执行完之后...

    dispatch_group包含wait

    以上两种方式都是模拟任务block内为异步操作的情况,方式一先执行的dispatch_group_notify里的代码,后执行的dispatch_group_async里的任务代码,这与我们的初衷相违背。如果任务block内为同步操作时,则无论哪种...

    【深入Java虚拟机(5)】多态性实现机制-静态分派与动

    每个Java对象都包含一个指向其类方法表的指针,当调用虚方法时,JVM会通过这个指针找到相应的方法实现并执行。非虚方法(如final或static方法)则不经过动态分派。 了解静态分派和动态分派对于理解Java的多态行为至...

    69丨访问者模式(下):为什么支持双分派的语言不需要访问者模式?1

    这与单分派(Single Dispatch)形成对比,单分派仅根据对象的类型决定调用哪个方法,而不考虑参数类型。在支持双分派的语言中,可以更灵活地处理动态绑定,从而在某些情况下不需要使用访问者模式。 主流的面向对象...

    dispatch_source

    `dispatch_source_set_data()`和`dispatch_source_get_data()`允许我们设置和获取数据源的数据值,用于实现自定义的数据过滤和同步机制。 7. **挂起与恢复** 可以使用`dispatch_suspend()`和`dispatch_resume()`...

    StrangeIOC使用讲解(Dispatch用法)

    在 StrangeIOCTest 文件中,你应该能看到如何设置和使用Dispatch的示例代码,包括Dispatch的实现、注册、事件触发以及与View、Controller和Model的交互等。 总的来说,StrangeIOC结合Dispatch提供了强大的组件化...

    前端项目-d3-dispatch.zip

    Dispatch在D3中是用来注册命名回调函数,并且能够传递参数调用这些函数,这对于处理事件和数据更新具有重要意义。 D3 dispatch 是一个事件调度系统,允许我们自定义事件和监听器,这在构建复杂的可复用组件时非常...

    grasshopper电池-dispatch案例

    关于grasshopper的基础练习,dispatch案例的电池资源。

    gcdTest下载图片 dispatch_async

    标题中的“gcdTest下载图片 dispatch_async”涉及到两个主要的iOS编程概念:GCD(Grand Central Dispatch)和异步图像下载。GCD是苹果为多核处理器优化并发编程提供的一种技术,而dispatch_async函数是GCD中用于在...

    COM IDispatch接口封装

    - **自动化**:IDispatch接口使得非编译时绑定成为可能,允许应用程序在运行时动态地发现和调用对象的属性和方法。 - **跨语言互操作**:不同编程语言的组件可以通过IDispatch接口进行通信,如VBScript和C++。 - ...

    ios demo,dispatch_once,单例模式的应用

    // 添加你需要的属性和方法 } ``` 在上面的例子中,`sharedInstance`是类方法,通过这种方式,我们可以随时随地获取到Singleton的唯一实例,而私有的初始化方法则防止了其他途径的实例化。 结合`dispatch_once`与...

Global site tag (gtag.js) - Google Analytics