Java中的方法内联介绍

目录
  • 1. 什么是方法内联
  • 2. 方法内联的重要性
  • 3. Java中方法内联的困难
    • 继承类型关系分析 CHA
  • 总结

1. 什么是方法内联

例如有下面的原始代码:

static class B {
    int value;
    final int get() {
        return value;
    }
}

public void foo() {
    y = b.get();
    // ...do stuff...
    z = b.get();
    sum = y + z;
}

我们首先要进行的就是方法内联,主要有下面两个目的:

去除方法调用的成本,如查找方法版本、建立栈帧。

为其他优化建立良好基础。

内联后代码如下:

public void foo() {
    y = b.value;
    // ...do stuff...
    z = b.value;
    sum = y + z;
}

后续,还可以进行冗余访问消除、复写传播、无用代码消除等优化操作。

2. 方法内联的重要性

方法内联是编译器最重要的优化手段,如果没有内联,多数其他优化都无法有效进行。例如下面这个例子:

public static void foo(Object obj){
    if (obj != null) {
        System.out.println("do something");
    }
}

public static void testInline(String[] args) {
    Object obj = null;
    foo(obj);
}

testInline()方法里其实全都是无用的代码,但是如果不做方法内联,就无法发现任何 Dead Code 的存在,因为分开看的话两个方法里面的操作可能都有意义。

3. Java中方法内联的困难

在 JVM 中,只有非虚方法,也就是使用invokespecial指令调用的私有方法、实例构造器、父类方法和使用invokestatic指令调用的静态方法才会在编译器进行解析。

而其他虚方法被invokevirtual指令调用,在调用时必须进行方法接收者的多态选择。对于一个虚方法,编译器静态地去做内联的时候很难确定应该使用哪个方法版本,这就造成了方法内联的困难。

继承类型关系分析 CHA

首先,JVM 引入了一种名为类型继承关系分析 CHA 的技术,这种技术用于在已加载的类中,确定某个接口是否有多于一种的实现、某个类是否存在子类、某个子类是否覆盖了父类的某个虚方法等信息。

编译器在进行内联时会分不同情况采取不同处理:

如果是非虚方法,那么就直接进行内联。

如果是虚方法,那么向 CHA 查询是否有多个目标版本可供选择。

如果只有一个版本,就直接内联,称为守护内联。但由于 Java 程序动态连接,不知道什么时候就会加载到新的类型而改变 CHA 的结论,所以要留好逃生门,假如程序后续执行中加载了导致继承关系发生变化的新类,那么必须抛弃已经编译的代码,退回到解释状态进行执行,或者重新编译。

如果有多个版本可供选择,那即时编译器使用内联缓存来缩减方法调用的开销。内联缓存是一个建立在目标方法正常入口之前的缓存。在未发生方法调用时,内联缓存为空。第一次调用发生后,缓存记录下方法接收者的版本信息,并且在每次进行调用前都检查版本。

如果每次调用的方法接收者版本是一样的,那称为单态内联缓存,通过缓存来调用,相比不内联只多了一次类型判断的开销。如果出现方法接收者不一致的情况,就退化为超多态内联缓存,开销相当于真正查找虚方法表来进行方法分派。当缓存未命中的时候,大多数JVM的实现时退化成超多态内联缓存,也有一些JVM选择重写单态内联缓存,就是更新缓存为新的版本。这样做的好处是以后还可能会命中,坏处是可能白白浪费一个写的开销。

总结

到此这篇关于Java中的方法内联介绍的文章就介绍到这了,更多相关Java方法内联内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java为何需要平衡方法调用与内联

    在 Java 中,方法调用一般通过 Virtual Call 还有 Classic Call. Classic Call 就是直接指向方法的地址,需要一次寻址到方法的地址,比直接执行代码慢. Virtual Call 需要通过 VMT(Virtual Method Table).这个VMT存储的是该class对象中所有的Virtual Method,程序运行的时候首先加载实例对象,然后通过实例对象找到VMT,通过VMT再找到对应的方法地址,再执行代码.所以比 Classic Call 更慢. J

  • Java中的方法内联介绍

    目录 1. 什么是方法内联 2. 方法内联的重要性 3. Java中方法内联的困难 继承类型关系分析 CHA 总结 1. 什么是方法内联 例如有下面的原始代码: static class B { int value; final int get() { return value; } } public void foo() { y = b.get(); // ...do stuff... z = b.get(); sum = y + z; } 我们首先要进行的就是方法内联,主要有下面两个目的:

  • Java中让界面内的时间及时更新示例代码

    我们经常在页面上显示时间,如果使用普通的时间日期设置方法,时间总是固定不变的.所以若要时间及时反馈当前计算机日期,我么可以通过多线程进行设计操作. 干货如下: 总结 到此这篇关于Java中让界面内的时间及时更新的文章就介绍到这了,更多相关Java界面内时间及时更新内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

  • Java中Range函数的简单介绍

    目录 前言 Range语法 IntStream范围的语法 LongStream范围的语法 Range函数在Java中是如何工作的? Java中的Range示例 前言 在Java中,Range方法在IntStream和LongStream类中都可用.在IntStream类中,它有助于返回函数参数范围内IntStream的顺序值.在该方法中,startInclusive(inclusive)和endExclusive(exclusive)是与增量步长一起使用的两个参数,如前所述,将包括起始值,并排除

  • Java中的FileInputStream 和 FileOutputStream 介绍_动力节点Java学院整理

    FileInputStream 和 FileOutputStream 介绍 FileInputStream 是文件输入流,它继承于InputStream. 通常,我们使用FileInputStream从某个文件中获得输入字节. FileOutputStream 是文件输出流,它继承于OutputStream. 通常,我们使用FileOutputStream 将数据写入 File 或 FileDescriptor 的输出流. FileInputStream 函数接口 FileInputStream

  • java中concat()方法的使用说明

    concat()方法介绍: 将几个字符串连接到一起. 例如: s = s.concat(str1);//将字符串str1接到字符串s后面 s = s.concat(str2);//将字符串str1接到字符串s后面 代码: public class Test { public static void main(String[] args){ String s = "厉害了,"; String str1 = "我的"; String str2 = "国!&qu

  • 详解Java中native方法的使用

    今天在网上学习时碰到有关于 native修饰符所修饰的方法,上网查了查,觉得很有意思记录一下 1.native简介 简单地讲,一个Native Method就是一个java调用非java代码的接口.一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C.这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数. native是与C++联合开发的时候用的!使用nat

  • java中join方法的理解与说明详解

    前言: java 中的 join() 方法在多线程中会涉及到,这个方法最初理解起来可能有点抽象,用一两次大概就懂了.简单说就是当前线程等待调用join方法的线程结束才能继续往下执行. 1. 举个例子 如下, MyRunnable 类是实现 Runnable 接口的多线程类,其run() 方法是一个计算,计算值存储在 result 字段,获取计算结果就必须等线程执行完之后调用 getResult() 获取 public class MyRunnable implements Runnable {

  • Java中equals()方法实例详解

    目录 equals()在哪里 Java中重写的equals() 在Java中比较的推荐方法 为什么要在我们自己的类中重写equals() 重写equals()的规范 重写equals()可能的误区 一般的equals()写法 附:java中equals()方法的正确使用 总结 equals()在哪里 首先我们知道Java中Object类是所有类的父类,它里面定义了equals()方法: public boolean equals(Object obj) { return (this == obj

  • 探索Java中private方法添加@Transactional事务未生效原因

    现在产品期望用户创建和保存逻辑分离:把User实例的创建和保存逻辑拆到两个方法分别进行.然后,把事务的注解 @Transactional 加在保存数据库的方法上. @Service public class StudentService { @Autowired private StudentMapper studentMapper; @Autowired private StudentService studentService; public void saveStudent(String

  • Java中枚举的实现原理介绍

    目录 基本概述 使用方式 条件选择 循环遍历 集合映射 常用方法 总结 基本概述 在 JDK1.5 之前,通过定义常量使用的都是:public static fianl.而枚举的设计,就是把相关的常量分组到一个枚举类型里,方便创建和管理. 比如我们要定义一个颜色常量: public enum Colour { RED, YELLOW, BLUE, GREEN } 这段代码通过底层编译后,实际创建了4个枚举对象: new Enum<EnumTest>("RED", 0); n

随机推荐