简单的解决方案是编写一个更实际的基准测试,该基准测试几乎有用,因此不会被优化掉。
有许多技巧可以混淆 JIT,但这些都不太可能对您有所帮助。
这是一个基准示例,其中该方法通过反射、MethodHandle 调用并编译为空。
import java.lang.invoke.*;
import java.lang.reflect.*;
public class Main {
public static void main(String... args) throws Throwable {
for (int j = 0; j < 5; j++) {
testViaReflection();
testViaMethodHandle();
testWithoutReflection();
}
}
private static void testViaReflection() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Method nothing = Main.class.getDeclaredMethod("nothing");
int runs = 10000000; // triggers a warmup.
long start = System.nanoTime();
Object[] args = new Object[0];
for (int i = 0; i < runs; i++)
nothing.invoke(null, args);
long time = System.nanoTime() - start;
System.out.printf("A call to %s took an average of %.1f ns using reflection%n", nothing.getName(), 1.0 * time / runs);
}
private static void testViaMethodHandle() throws Throwable {
MethodHandle nothing = MethodHandles.lookup().unreflect(Main.class.getDeclaredMethod("nothing"));
int runs = 10000000; // triggers a warmup.
long start = System.nanoTime();
for (int i = 0; i < runs; i++) {
nothing.invokeExact();
}
long time = System.nanoTime() - start;
System.out.printf("A call to %s took an average of %.1f ns using MethodHandle%n", "nothing", 1.0 * time / runs);
}
private static void testWithoutReflection() {
int runs = 10000000; // triggers a warmup.
long start = System.nanoTime();
for (int i = 0; i < runs; i++)
nothing();
long time = System.nanoTime() - start;
System.out.printf("A call to %s took an average of %.1f ns without reflection%n", "nothing", 1.0 * time / runs);
}
public static void nothing() {
// does nothing.
}
}
印刷
A call to nothing took an average of 6.6 ns using reflection
A call to nothing took an average of 10.7 ns using MethodHandle
A call to nothing took an average of 0.4 ns without reflection
A call to nothing took an average of 4.5 ns using reflection
A call to nothing took an average of 9.1 ns using MethodHandle
A call to nothing took an average of 0.0 ns without reflection
A call to nothing took an average of 4.3 ns using reflection
A call to nothing took an average of 8.8 ns using MethodHandle
A call to nothing took an average of 0.0 ns without reflection
A call to nothing took an average of 5.4 ns using reflection
A call to nothing took an average of 13.2 ns using MethodHandle
A call to nothing took an average of 0.0 ns without reflection
A call to nothing took an average of 4.9 ns using reflection
A call to nothing took an average of 8.7 ns using MethodHandle
A call to nothing took an average of 0.0 ns without reflection
我曾假设 MethodHandles 比反射更快,但事实并非如此。