深入拆解 Java 虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
87446 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
模块四:黑科技 (3讲)
深入拆解 Java 虚拟机
15
15
1.0x
00:00/00:00
登录|注册

21 | 方法内联(下)

final修饰符
假设
类加载
Sea-of-Nodes IR
数据流分析
不完整的类型Profile
完整的类型Profile
类型Profile
类型比较
基于类层次分析
基于类型推导
实践环节
条件去虚化
完全去虚化
条件去虚化
完全去虚化
总结与实践
方法内联

该思维导图由 AI 生成,仅供参考

在上一篇中,我举的例子都是静态方法调用,即时编译器可以轻易地确定唯一的目标方法。
然而,对于需要动态绑定的虚方法调用来说,即时编译器则需要先对虚方法调用进行去虚化(devirtualize),即转换为一个或多个直接调用,然后才能进行方法内联。
即时编译器的去虚化方式可分为完全去虚化以及条件去虚化(guarded devirtualization)。
完全去虚化是通过类型推导或者类层次分析(class hierarchy analysis),识别虚方法调用的唯一目标方法,从而将其转换为直接调用的一种优化手段。它的关键在于证明虚方法调用的目标方法是唯一的。
条件去虚化则是将虚方法调用转换为若干个类型测试以及直接调用的一种优化手段。它的关键在于找出需要进行比较的类型。
在介绍具体的去虚化方式之前,我们先来看一段代码。这里我定义了一个抽象类 BinaryOp,其中包含一个抽象方法 apply。BinaryOp 类有两个子类 Add 和 Sub,均实现了 apply 方法。
abstract class BinaryOp {
public abstract int apply(int a, int b);
}
class Add extends BinaryOp {
public int apply(int a, int b) {
return a + b;
}
}
class Sub extends BinaryOp {
public int apply(int a, int b) {
return a - b;
}
}
下面我便用这个例子来逐一讲解这几种去虚化方式。

基于类型推导的完全去虚化

基于类型推导的完全去虚化将通过数据流分析推导出调用者的动态类型,从而确定具体的目标方法。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了即时编译器中方法内联的优化技术,包括完全去虚化和条件去虚化两种方法。通过类型推导和类层次分析,完全去虚化能够将虚方法调用转换为直接调用,从而提高程序性能。而条件去虚化则通过类型比较和类型测试,结合类型Profile,实现对虚方法调用的优化。文章通过具体的代码示例和IR图分析,详细解释了各种优化方式的原理和应用场景。此外,还讨论了即时编译器在优化过程中的假设注册和类型Profile记录的重要性,以及对接口方法调用的处理方式。通过深入浅出的方式,本文为读者介绍了方法内联的优化技术及其实现原理,对于理解即时编译器的优化过程具有一定的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Java 虚拟机》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(20)

  • 最新
  • 精选
  • 永烁星光
    IR 图分析看了这三篇,好几次,现在还是不甚明白,

    作者回复: 如果都看懂了,可以考虑来我司,或者阿里的JIT专家职位 :) 一般来说,了解这些优化针对怎样的代码模式,会转化为怎样的代码就可以啦。至于IR图,主要是用来辅助理解具体的优化过程。

    2018-09-10
    3
    14
  • Scott
    是每个对象有type profile的限制么?

    作者回复: 每条类型相关字节码,如invokeinterface invokevirtual checkcast instanceof等

    2018-09-10
    1
  • Scott
    我也不清楚,什么时候可以有完整的profile,什么时候是不完整的

    作者回复: 回了原提问,这里复制一下: 每个字节码的type profile有数量限制,比如默认情况下只能存两个不同的动态类型。如果收集profile过程中来了三个不同的动态类型,那么JVM不能全部记下来,因此即时编译器看到的type profile是不完整的。

    2018-09-09
  • Void_seT
    老师,想请教一下,“类型Profile”完整还是不完整,是如何判断的?

    作者回复: 每个字节码的type profile有数量限制,比如默认情况下只能存两个不同的动态类型。如果收集profile过程中来了三个不同的动态类型,那么JVM不能全部记下来,因此即时编译器看到的type profile是不完整的。

    2018-09-08
  • 感觉跟不上了,先过吧! 已经拉下两节了,日后回头再看看。 现在仅明白,方法内联-是编译器的一种代码优化手段,会根据不同代码调用方式有不同的优化方式,目的都是为了提高JVM的效率,根本方式,我认为就是采用取巧的方式,提前判断出来可以少做一些事情,然后先提前做一些准备,整体的时间和空间成本会降下来。 另外,提供小建议,雨迪能否对于这种比较比较抽象的知识,来点生动形象的比喻以便帮助消化,之前在知乎看到一篇关于锁的文章,全篇通过生动形象的比喻讲解锁的本质、分类、各种锁的特点,读起来一下子就明白了。
    2018-09-12
    3
    30
  • Joker
    漫漫长路,这JAVA一门语言就要如此深究,真特么知无涯
    2019-08-16
    11
  • 西门吹牛
    方法内联就是将调用的目标方法,内联到调用者方法里面,以避免目标方法的重复调用带来的开销,但是在内联时,如果目标方法,完全确定,也就是说,目标方法的调用是唯一的,那么直接内联就可, 但是由于Java的多态特性,基于接口而非实现编程等,导致目标方法的调用的需要在运行时确定,也就是虚方法的调用在即时编译阶段无法确定唯一调用的目标方法版本,而内联是在即时编译阶段。 一部分方法的符号引用在编译阶段就可以确定唯一的调用版本,但是一部分必须在运行时才能将符号引用替换为直接引用,这就导致,在即时编译器进行内联时,这部分方法没法确定唯一的调用版本,于是就有去虚化手段,把虚方法调用通过一定的去虚化手段,直接替换为直接调用,保证内联后的方法在实际运行时不会出错。 去虚化的手段,只能尽量保证虚方法的调用能直接替换为直接调用,只有准确的替换,才能体现出内联的优势,如果实在确定不了虚方法调用的准确版本,那么就去优化,也就是不内联了。 基于类型的去虚化:通过对象的静态类型,实际类型,一些重载,重写方法的调用,其实编译器能通过具体的数据类型,进行识别。可以说一旦识别,就准确无误。 基于层次的去虚化:完全依赖于jvm类的加载,基于只加载一个类的假设。适用场景很受限。 基于条件的去虚化:依赖于分层编译时收集的数据。 总的来说,内联带来的程序运行的性能提升要远远大于内联的成本。方法内联,为的就是把即时编译的性能发挥到极致。都是为性能考虑的。
    2020-07-20
    8
  • 一少爷
    为什么后面留言的人越来越少了,我觉得后面这些也很关键很有趣呀。对思想的提升很有帮助的。
    2019-02-27
    6
  • 随心而至
    免费的才是最贵的,享受便利的同时,想搞明白确实不容易,我只有个大的概念。感觉这可以类比CPU里面的冒险与预测来理解,都是基于某种方式来优化,让程序跑的更快些。
    2019-10-25
    4
  • 饭粒
    基于类型推导的完全去虚化 基于类层次分析的完全去虚化 条件虚拟化 目录挺清楚的,极客时间的文章出个标题侧栏就更好了。
    2019-12-24
    2
收起评论
显示
设置
留言
20
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部