JVM系列-方法调用的原理

最近重新看了一些JVM方面的笔记和资料,收获颇丰,尤其解决了长久以来心中关于JVM方法管理的一些疑问。下面介绍一下JVM中有关方法调用的知识。

目的

方法调用,目的是选择方法正确的执行版本,也就是找到方法的入口地址。

方法调用指令

方法调用的字节码指令一共有五种,分别是:

  1. invokestatic:

    • 类方法:static
  2. invokespecial:
    • 方法:实例构造器
    • 私有方法:private
    • 父类中的方法
  3. invokevirtual
    • 虚方法
    • final修饰的方法
  4. invokeinterface
    • 接口方法
  5. invokedynamic
    • 用于动态语言支持

方法分类

非虚方法:

  1. 特点:

    • 在类加载时期,有些方法的调用版本已经能够确定,在类加载-解析阶段,把这些方法的符号引用替换为直接引用(即入口地址)。
  2. 包括:
    1. 类方法
    2. 方法
    3. 私有方法
    4. 父类中的方法
    5. final方法

即由invokestatic、invokespecial指令调用的方法,以及final修饰的方法,属于非虚方法

虚方法:

  1. 特点:

    • 在类加载时期,无法确定方法最终的调用版本
  2. 包括:
    • 非非虚方法

即由invokevirtual调用的方法,除了final修饰的之外,都是虚方法

调用方式

解析

  1. 定义:对于非虚方法,在类加载的时候已经能确定这些方法的调用版本,在类加载的解析阶段把符号引用替换为直接引用,就是方法的入口地址
  2. 范围:非虚方法

分派

静态分派

  1. 定义:在编译期,根据方法涉及的引用类型(包括参数列表的引用类型和方法的调用者的引用类型)来确定方法调用的(初步)版本,并把相应的符号引用放在字节码指令中,这个步骤叫做静态分派
  2. 范围:虚方法、非虚方法
  3. 应用:方法的重载

动态分派

  1. 定义:在运行时,根据对象的实际类型来确定方法的调用版本,这个步骤叫做动态分派
  2. 范围:虚方法
  3. 应用:方法的重写
  4. 原理:
    1. 方法调用的部分字节码指令:

      1. aload:在调用方法时,总是有该指令将实际类型的对象引用入栈
      2. 加载方法参数
      3. invoketual
        1. 先在栈顶取到实际类型的对象引用
        2. 根据方法的符号引用在本类中寻找,找不到的话依次向上在父类中找该方法
    2. 优化实现:在虚拟机中,虚方法表是动态分派的一种优化实现
      1. 虚方法表:

        1. 定义:类信息的一种,在类加载-准备阶段完成初始化,存放在方法区
        2. 特点:
          1. 虚方法表中存储方法的入口地址
          2. 子类中继承但未重写的方法,在子类的虚方法表中存放的入口地址,就是父类的虚方法表中的入口地址,指向父类的实现
          3. 在父子类中,相同符号引用的方法(重写)的方法,其在各自虚方法表中的索引相同
      2. 解释动态分派:
        1. 根据字节码指令的符号引用,找到该方法在父类虚方法表中的索引
        2. 切换到实际类型的虚方法表,通过该索引,找到方法的入口地址

总结

主要内容在于方法调用的方式,即如何找到方法的入口地址的过程。

在这三种方式中,静态分派发生在编译期,解析发生在类加载时,动态分派发生在运行时。针对三者作用对象的不同可知,静态分派是可以和解析或动态分派同时发生的,如类方法依然可以重载,重写的方法也可以重载。但解析和动态分派是不可共存的。

以上是我的分享,如有疑问或错误,请指出,一起学习。

参考

《深入理解Java虚拟机_JVM高级特性与最佳实践-第3版》

JVM系列-方法调用的原理的更多相关文章

  1. 深入探究JVM之方法调用及Lambda表达式实现原理

    @ 目录 前言 正文 解析 分派 静态分派 动态分派 单分派和多分派 动态分派的实现 Lambda表达式的实现原理 MethodHandle 总结 前言 在最开始讲解JVM内存结构的时候有简单分析过方 ...

  2. 以方法调用的原理解释Ruby中“puts ‘Hello‘”

    这里尽管缺少消息发送所需要的点(.)以及该消息的显示接收者,却依然发送了消息puts并传递了参数“Hello”给一个对象:默认对象self.在程序运行期间,虽然作为self的对象通过特定规则发生改变, ...

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

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

  4. Java方法调用机制

    最近在编程时,修改方法传入对象的对象引用,并没有将修改反映到调用方法中.奇怪为什么结果没有变化,原因是遗忘了Java对象引用和内存分配机制.本文介绍3个点: ① 该问题举例说明 ② 简要阐述Java内 ...

  5. 聊聊 C# 中的多态底层 (虚方法调用) 是怎么玩的

    最近在看 C++ 的虚方法调用实现原理,大概就是说在 class 的首位置存放着一个指向 vtable array 指针数组 的指针,而 vtable array 中的每一个指针元素指向的就是各自的 ...

  6. JVM方法调用

    当我们站在JVM实现的角度去看方法调用的时候,我们自然会想到一种分类: 1.编译代码的时候就知道是哪个方法,永远不会产生歧义,例如静态方法,private方法,构造方法,super方法. 2.运行时才 ...

  7. java方法调用之动态调用多态(重写override)的实现原理——方法表(三)

    上两篇篇博文讨论了java的重载(overload)与重写(override).静态分派与动态分派.这篇博文讨论下动态分派的实现方法,即多态override的实现原理. java方法调用之重载.重写的 ...

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

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

  9. JAVA的List接口的remove重载方法调用原理

    前言 说真的,平常看源码都是自己看完自己懂,很少有写出来的冲动. 但是在写算法的时候,经常用到java中各种集合,其中也比较常用到remove方法. remove有重载函数,分别传入参数是索引inde ...

随机推荐

  1. 贪吃蛇游戏(printf输出C语言版本)

    这一次我们应用printf输出实现一个经典的小游戏—贪吃蛇,主要难点是小蛇数据如何存储.如何实现转弯的效果.吃到食物后如何增加长度. 1 构造小蛇 首先,在画面中显示一条静止的小蛇.二维数组canva ...

  2. 论logstash的玩法(ELK)

    本篇文章采用的采用的是logstash-7.7.0版本,主要从如下几个方面介绍 1.logstash是什么,可以用来干啥 2.logstash的基本原理是什么 3.怎么去玩这个elk的组件logsta ...

  3. VMWare12安装CentOS7操作系统并搭建GitLab环境【1】

    查看了网上这方面的资料,发现都比较复杂,自己到官方网站上查询,并实际动手安装了一下,发现还是比较简单的. 1.VMWare Workstation 12 Professinal安装 2.安装64位Ce ...

  4. jmeter跨线程组传值和jmeter跨线程组调用

    Jmeter的线程组之间是独立的,用Jmeter做接口测试或者是性能测试时,经常会涉及到多个线程组.那么如何将A线程组返回的变量信息提取后,传递给B,C线程组使用呢?这里以已登录接口返回的access ...

  5. Python实现监测抖音在线时间,实时记录一个人全天的在线情况

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:小dull鸟 今天给大家分享一篇有趣的文章,灵感来自于前几天与室友的 ...

  6. 完美解决asp.net core 3.1 两个AuthenticationScheme(cookie,jwt)共存在一个项目中

    内容 在我的项目中有mvc controller(view 和 razor Page)同时也有webapi,那么就需要网站同时支持2种认证方式,web页面的需要传统的cookie认证,webapi则需 ...

  7. netty--helloword程序

    1.使用netty需要使用到下面的java包 netty-all-5.0.0.Alpha2.jar 我们来看下面具体的代码 1. 创建一个ServerBootstrap实例 2. 创建一个EventL ...

  8. java scoket aIO 通信

    AsynchronousServerSocketChannel assc.accept(this, new ServerCompletionHandler()); 第一个参数是服务器的处理类,第二个参 ...

  9. Python 偏函数用法全方位解析

    Python的functools模块中有一种函数叫“偏函数”,自从接触它以来,发现确实是一个很有用且简单的函数,相信你看完这篇文章,你也有相见恨晚的感觉. 我们都知道,函数入参可以设置默认值来简化函数 ...

  10. Kubernetes 两步验证 - 使用 Serverless 实现动态准入控制

    作者:CODING - 王炜 1. 背景 如果对 Kubernetes 集群安全特别关注,那么我们可能想要实现这些需求: 如何实现 Kubernetes 集群的两步验证,除了集群凭据,还需要提供一次性 ...