参考反射调用简单性能测试与调优
直接调用性能:
public class Test {
public static void target(int i) { }
public static void main(String[] args) throws Exception {
long current = System.currentTimeMillis();
for (int i = 1; i <= 2_000_000_000; i++) {
if (i % 100_000_000 == 0) {
long temp = System.currentTimeMillis();
System.out.println(temp - current);
current = temp;
}
target(128);
}
}
}
82
82
84
85
81
1.Lambda表达式调用性能
import java.util.function.IntConsumer;
public class TestV6 {
public static void target(int i) { }
public static void main(String[] args) throws Exception {
long current = System.currentTimeMillis();
for (int i = 1; i <= 2_000_000_000; i++) {
if (i % 100_000_000 == 0) {
long temp = System.currentTimeMillis();
System.out.println(temp - current);
current = temp;
}
((IntConsumer) j -> TestV6.target(j)).accept(128);
// ((IntConsumer) TestTestV6::target).accept(128);
}
}
}
结果:
111
110
110
110
113
((IntConsumer) TestTestV6::target).accept(128)调用如下:
111
110
113
112
110
与直接调用性能差别不大,说明JIT能够对内联Lambda和方法引用的accept操作,最终优化为空操作。
2.捕获变量版本
import java.util.function.IntConsumer;
public class TestV7 {
public static void target(int i) { }
public static void main(String[] args) throws Exception {
int x = 2;
long current = System.currentTimeMillis();
for (int i = 1; i <= 2_000_000_000; i++) {
if (i % 100_000_000 == 0) {
long temp = System.currentTimeMillis();
System.out.println(temp - current);
current = temp;
}
((IntConsumer) j -> TestV7.target(x + j)).accept(128);
}
}
}
82
83
83
83
83
性能跟直接调用一样,JIT的逃逸分析将新建的适配器类实例优化掉了。
关闭逃逸分析,-XX:-DoEscapeAnalysis
305
301
339
318
308
逃逸分析会生效,需要满足两个条件:
1)invokedynamic指令所执行的MethodHandle能够内联
2)对accept方法的调用也能够内联
3.MethodHandle调用
public class TestV8 {
public static void target(int i) { }
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(void.class, int.class);
MethodHandle mh = l.findStatic(TestV8.class, "target", t);
long current = System.currentTimeMillis();
for (int i = 1; i <= 2_000_000_000; i++) {
if (i % 100_000_000 == 0) {
long temp = System.currentTimeMillis();
System.out.println(temp - current);
current = temp;
}
mh.invokeExact(128);
}
}
}
325
323
321
325
324
MethodHandle与Lambda表达式或方法引用的差别在于,JIT无法将该MethodHandle识别为常量,从而无法进行内联。
4.将MethodHandle定义为final
public class TestV9 {
public static void target(int i) {
}
static final MethodHandle mh;
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(void.class, int.class);
mh = l.findStatic(TestV9.class, "target", t);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Throwable {
long current = System.currentTimeMillis();
for (int i = 1; i <= 2_000_000_000; i++) {
if (i % 100_000_000 == 0) {
long temp = System.currentTimeMillis();
System.out.println(temp - current);
current = temp;
}
mh.invokeExact(128);
}
}
}
83
83
83
83
84
定义为常量后,JIT即可进行内联。
5.final MyCallSite无法内联
public class TestV10 {
public static void target(int i) {
}
public static class MyCallSite {
public final MethodHandle mh;
public MyCallSite() {
mh = findTarget();
}
private static MethodHandle findTarget() {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(void.class, int.class);
return l.findStatic(TestV10.class, "target", t);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
private static final MyCallSite myCallSite = new MyCallSite();
public static void main(String[] args) throws Throwable {
long current = System.currentTimeMillis();
for (int i = 1; i <= 2_000_000_000; i++) {
if (i % 100_000_000 == 0) {
long temp = System.currentTimeMillis();
System.out.println(temp - current);
current = temp;
}
myCallSite.mh.invokeExact(128);
}
}
}
299
310
302
298
301
可以看出自定义的调用点,即使定义为final,也无法进行内联
6.继承ConstantCallSite即可内联
public class TestV11 {
public static void target(int i) {
}
public static class MyCallSite extends ConstantCallSite {
public MyCallSite() {
super(findTarget());
}
private static MethodHandle findTarget() {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(void.class, int.class);
return l.findStatic(TestV11.class, "target", t);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
public static final MyCallSite myCallSite = new MyCallSite();
public static void main(String[] args) throws Throwable {
long current = System.currentTimeMillis();
for (int i = 1; i <= 2_000_000_000; i++) {
if (i % 100_000_000 == 0) {
long temp = System.currentTimeMillis();
System.out.println(temp - current);
current = temp;
}
myCallSite.getTarget().invokeExact(128);
}
}
}
82
82
83
83
85
可以看出ConstantCallSite及子类被特殊对待了,可以进行内联。