点击上方蓝字,记得关注我们!
JVM之JIT技术(二):内联
引言
在JIT技术上,有很多种优化技术,其中内联是一种非常常见的技术,在本文中将会详细的介绍这技术,同时使用数字对比,更加形象的突出内联的运行时间。
01
在上一篇文章《JVM之JIT优化技术(一)》中,我们使用了大量的篇幅讲解了JIT技术的由来和一些简单的优化步骤,以及优化例子,本文将详细讲解JIT技术之一:内联。
首先,需要指出的是我的测量工作是突出在JVM上的可能性,并不是产生精确的数字。一些优化能被简单的测量,然而其他的并不能这样。一些测量可能被一些我并没有考虑在内的优化或者是通过一些聪明的JVM策略所扭曲,如果一些技术还没有完全可用这些JVM策略会提供优雅的降级。此外,优化通常是协同工作的,实际上它们服务于更大的应用程序,而不是像我创建的那些微基准测试那样进行微调。
测量是使用JMH(Java微基准测试工具)完成的,JMH是一个库,它帮助构建和正确运行各种基准测试。
02
内联通过用被调用方法的主题替换方法调用,消除了方法调用的成本。将相关代码移到一起也能进一步优化,对于单独的方法这是不可能的,例如子表示式消除或堆分配消除。
当然,内联任何东西是不切实际的。默认的,内联发生哪些编译大小小于35byte的方法,或者小于325byte的热门方法中。这一阈值可以被改变通过设置-XX:MaxInlineSize和
-XX:FreqInlineSize这两个参数。
举个例子,下面的add方法最终将会被内联:
代码一:
public void inliningExample() {
int sum = add(100, 2000);
// Do something with sum.
}
public static int add(int a, int b) {
return a + b;
}
大小限制考虑断言语句,即使它们是禁用的。默认情况下是这样的。
我进行了一系列的实验,在这些实验中,我使用越来越大的add方法来测量上面的例子性能。(通过简单的添加断言表达式,我似的这个方法变得更大)
上面的图清楚地显示了再确定的方法大小之后性能的中断。
内联也受多态的制约。当分配一个方法调用时,JVM检测这个方法有多少种不同的实现。
1.如果只有一个,这将会是单一调度
2.如果有两个实现,它被称为双态调度。
3.如果有超过两个方法实现可以接收方法调用,被称为变形调度。
需要注意的是,单一调度和双态调度能被内联,变形调度不能被内联。
代码二:
public abstract interface Animal { public String say();}
public class Dog implements Animal {public String say() {return "bark";}}
public class Cat implements Animal {public String say() {return "meow";}}
public class Octopus implements Animal {public String say() {return "...";}}
// More animals if needed...
public void inlining() {
Animal a;
if (i == 0) { // i is a random number from 0 to 2.
a = new Dog();
}
else if (i == 1) {
a = new Cat();
}
else if (i == 2) {
a = new Octopus();
}
else {
throw new NullPointerException();
}
String result = a.say();
// Do something with the result.
}
运行上面的代码去验证刚才提到的,你会发现,使用单一调度的速度差不多是其他两种调度的两倍。
0
3
总之,内联可以优化程序,提高程序执行速度。如果内联的方法有一种实现称为单一调度,如果有两种实现,称为双态调度,如果多余两种,称为变形调度。且单一调度最快。
▼
往期精彩回顾
▼
扫描二维码
关注我们吧
你点的每个赞,我都认真当成了喜欢