虚方法调用

Java 里所有非私有实例方法调用都会被编译成 invokevirtual 指令,而接口方法调用会被编译成 invokeinterface 指令。这两种指令,均属于 Java 虚拟机中的虚方法调用。

动态绑定:Java 虚拟机需要根据调用者的动态类型,来确定虚方法调用的目标方法。

静态绑定:调用静态方法的 invokestatic 指令,以及用于调用构造器,私有实例方法和超类非私有实例方法的 invokestatic 指令。如果虚方法调用指向一个标记为 final 的方法,那么 Java 虚拟机也可以静态绑定该虚方法调用的目标方法。

关于动态绑定,Java 虚拟机采用一种空间换区时间的策略来实现动态绑定。它为每个类生成一张方法表,用以快速定位目标方法。

方法表

类的加载过程中,到了准备阶段,除了为静态字段分配内存之外,还会构造于该类相关联的方法表。方法表是 Java 虚拟机实现动态绑定的关键所在。

下面以 invokevirtual 所使用的虚方法表(virtual method table,vtable)为例介绍方法表的用法。

方法表本质上是一个数组,每个数组元素指向一个当前类及其祖先类中非私有的实例方法。这些方法可以是具体的可执行的方法,也可以是没有想用字节码的抽象方法。

方法表满足两个特质:一,子类方法表中包含父类方法表中的所有方法;二,子类方法在方法表中的索引值,与它所重写的父类方法的索引值相同。

类的加载过程中,方法调用指令中的符号引用会在执行之前解析成实际引用。对于静态绑定的方法调用而言,实际引用将指向具体的目标方法。对于动态绑定的方法调用而言,实际引用则是方法表的索引值(并不仅仅是索引值。)。

在执行过程中,Java 虚拟机将获取调用者的十几类型,并在该实际类型的虚方法表中,根据索引值获得目标方法。这个过程就是动态绑定。

动态绑定与静态绑定相比,仅仅是多出几个内存解引用的操作:访问栈上的调用者,读取调用者的动态类型,读取该类型的方法表,读取方法表中某个索引对应的目标方法。对于创建并初始化 Java 栈帧来说,这几个内存解引用操作的开销简直可以忽略不计。

利用方法表的方式优化虚方法的调用,这种优化仅存在于解释执行中,或者即时编译代码的最坏情况中。即时编译还有另外两种性能更好的优化手段:内联缓存和方法内联。

内联缓存

内联缓存是一种加快动态动态绑定的优化技术。它能够缓存虚方法调用中调用者的动态类型,以及该类型所对应的目标方法。在之后的执行过程中,如果碰到已缓存的类型,内联缓存便会直接调用该类型所对应的目标方法。如果没有缓存,则会退化为基于方法表的动态绑定。

在针对多态的优化手段中,需要了解一下三个术语:
1:单态,指的是仅有一种状态的情况。
2:多态,指的是有限数量种多态的情况。
3:超多态,指的是更多种状态的情况。通常用一个具体值区分多态和超态,数值之下为多态,数值之上为超态。

对于内联缓存,有上述三种对用的内联缓存。

单态内联缓存:只缓存一种动态类型以及它所对应的目标方法。

多态内联缓存:缓存多个动态类型以及它所对应的目标方法。使用的时候,需要遍历多个缓存数据,如果命中则调用对应的目标方法。在时间操作中,大部分的虚方法调用均是单态的,也就是只用一种动态类型。为了节省内存空间,Java 虚拟机只采用单态的内联缓存。如果没有命中缓存,Java 虚拟机就要重新使用方法表进行动态绑定。

这种情况下,对于内联缓存,我们有两种选择。一:替换单态内联缓存中的记录。替换后,要保证方法调用者的动态类型与方法调用的类型保持一致,从而能够有效的利用内联缓存。最坏的情况是两种不同的动态类型交替调用,对于内联缓存就只有写的额外开销,并没有利用缓存来提高性能。二:劣化为超多态状态。这也是 Java 虚拟机的具体实现方式。这种情况下,其实已经放弃了缓存优化,直接访问方法表来动态绑定目标方法。于内联缓存相比,方法表牺牲了优化的机会,但是却节省了写内存的额外开销。

问答

Q:为什么调用超类非私有实例方法会属于静态绑定呢?

通过super关键字来调用父类方法,本意就是想要调用父类的特定方法,而不是根据具体类型决定目标方法。

总结

本文创作灵感来源于 极客时间 郑雨迪老师的《深入拆解 Java 虚拟机》课程,通过课后反思以及借鉴各位学友的发言总结,现整理出自己的知识架构,以便日后温故知新,查漏补缺。

关注本人公众号,第一时间获取最新文章发布,每日更新一篇技术文章。

04 JVM是如何执行方法调用的(下)的更多相关文章

  1. 04 JVM是如何执行方法调用的(上)

    重载和重写 重载:同一个类中定义名字相同的方法,但是参数类型或者参数个数必须不同. 重载的方法在编译过程中就可完成识别.具体到每一个方法的调用,Java 编译器会根据所传入参数的生命类型来选取重载方法 ...

  2. JVM(十二):方法调用

    JVM(十二):方法调用 在 JVM(七):JVM内存结构 中,我们说到了方法执行在何种内存结构上执行:Java 方法活动在虚拟机栈中的栈帧上,栈帧的具体结构在内存结构中已经详细讲解过了,下面就让我们 ...

  3. 多态:JVM是如何进行方法调用的

    在我们平时的工作学习中写java代码时,如果我们在同一个类中定义了两个方法名和参数类型都相同的方法时,编译器会直接报错给我们.还有在代码运行的时候,如果子类定义了一个与父类完全相同的方法的时候,父类的 ...

  4. 深入解析多态和方法调用在JVM中的实现

    深入解析多态和方法调用在JVM中的实现 1. 什么是多态 多态(polymorphism)是面向对象编程的三大特性之一,它建立在继承的基础之上.在<Java核心技术卷>中这样定义: 一个对 ...

  5. 第31篇-方法调用指令之invokevirtual

    invokevirtual字节码指令的模板定义如下: def(Bytecodes::_invokevirtual , ubcp|disp|clvm|____, vtos, vtos, invokevi ...

  6. 图解JVM执行引擎之方法调用

    一.方法调用 方法调用不同于方法执行,方法调用阶段的唯一任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程.Class文件的编译过程中不包括传统编译器中的连接步骤,一 ...

  7. jvm 字节码执行 (一)方法调用

    “虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上,而虚拟机的执行引擎是 由自己实现的,因此可以自行制定指令集 ...

  8. JVM方法调用过程

    JVM方法调用过程 重载和重写 同一个类中,如果出现多个名称相同,并且参数类型相同的方法,将无法通过编译.因此,想要在同一个类中定义名字相同的方法,那么它们的参数类型必须不同.这种方法上的联系就是重载 ...

  9. Spring杂谈 | 从桥接方法到JVM方法调用

    前言 之所以写这么一篇文章是因为在Spring中,经常会出现下面这种代码 // 判断是否是桥接方法,如果是的话就返回这个方法 BridgeMethodResolver.findBridgedMetho ...

随机推荐

  1. UOJ#210. 【UER #6】寻找罪犯 2-sat

    #210. [UER #6]寻找罪犯 链接:http://uoj.ac/problem/210 想法:2-sat模型.每个人拆点,分别表示为犯人.非犯人.每个句供词拆点,分别表示真话.假话.供词与对应 ...

  2. CAsyncSocket create创建套接字失败

    解决方法: 在继承CAsyncSocket 类的子类的构造函数中加入以下代码: if (!AfxSocketInit()) AfxMessageBox(IDP_SOCKETS_INIT_FAILED) ...

  3. ehcache常用API整理

    鉴于csdn的blog的不稳定, 及混乱的编辑器, 和无上传功能, 遂决定彻底投诚javaeye的blog. 数月前整理的一个东西, 作为cache的扫盲文档.参考了它的官方文档. 对ehcache感 ...

  4. 2018.2.5 PHP如何写好一个程序用框架

    随着PHP标准和Composer包管理工具的面世,普通开发者撸一个框架已经不再是什么难事了. 无论是路由管理.ORM管理.还是视图渲染都有许许多多优秀的包可以使用.我们就像堆积木一样把这些包用comp ...

  5. java 学习集锦

    java学习系列 http://www.cnblogs.com/skywang12345/category/455711.html

  6. python读取txt写入txt

    http://www.cnblogs.com/allenblogs/archive/2010/09/13/1824842.html

  7. 牛客小白月赛5 F 圆(circle) 【欧拉定理】

    题目连接: https://www.nowcoder.com/acm/contest/135/F 签到题来了,送你们一个Python秒的题. Apojacsleam来到了OI大陆,经过了连年征战,成为 ...

  8. (77)zabbix主动、被动检测的详细过程与区别

    zabbix agent检测分为主动(agent active)和被动(agent)两种形式,主动与被动的说法均是相对于agent来讨论的.简单说明一下主动与被动的区别如下: 主动:agent请求se ...

  9. mysql六:数据备份、pymysql模块

    一 IDE工具介绍 生产环境还是推荐使用mysql命令行,但为了方便我们测试,可以使用IDE工具 掌握: #1. 测试+链接数据库 #2. 新建库 #3. 新建表,新增字段+类型+约束 #4. 设计表 ...

  10. 用dump为什么总会在前面出现/path/debug.php:193

    解决方案,在php.ini中的xdebug中加一行代码:xdebug.overload_var_dump=1