为了更清楚地了解其中区别,我编写了代码对其执行速度进行测试。分别在有预热和无预热的状态下,对Reflection机制在极多次调用下,jvm似乎有能力将其自动编译优化,速度比MethodHandle更快。而在调用较少的情况下,MethodHandle有其轻量级的优势。
以下为测试代码:
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.math.BigDecimal; public class GoodestStatic { public static final int WARM_UP = 500_000; //预热次数 public static int TEST_TIME= Integer.MAX_VALUE; //执行次数 public volatile static boolean NEED_WARM_UP = false; //是否预热 public static final int good(int num){ return num*5+1; } public static void main(String[] args) throws Throwable{ if(args.length > 0){ NEED_WARM_UP = "true".equalsIgnoreCase(args[0]); } if(args.length > 1){ TEST_TIME = Integer.parseInt(args[1]); } System.out.println("WARM_UP ="+NEED_WARM_UP+",TEST_TIME="+TEST_TIME); test0(); //直接方法调用 test1(); //Method反射调用 test2(); //MethodHandle的invoke()调用 test3(); //MethodHandle的invokeExact调用 } private static void test0() throws Exception { int sum = 0; if(NEED_WARM_UP) for(int i=0; i<WARM_UP;i++){ sum += good(i); } long start = System.nanoTime(); for(int i=0; i<TEST_TIME;i++){ sum += good(i); } long end = System.nanoTime(); System.out.println("Result ="+sum+",Test0 cost "+(end -start)+"ns."); BigDecimal bd = new BigDecimal(end-start); bd = bd.divide(new BigDecimal(TEST_TIME),20,BigDecimal.ROUND_HALF_UP); System.out.println("per Time cost "+bd+" ns"); } private static void test1() throws Exception { Method m = GoodestStatic.class.getDeclaredMethod("good", int.class); int sum = 0; if(NEED_WARM_UP) for(int i=0; i<WARM_UP;i++){ sum += (int)m.invoke(null, i); } long start = System.nanoTime(); for(int i=0; i<TEST_TIME;i++){ sum += (int)m.invoke(null, i); } long end = System.nanoTime(); System.out.println("Result ="+sum+",Test1 cost "+(end -start)+"ns."); BigDecimal bd = new BigDecimal(end-start); bd = bd.divide(new BigDecimal(TEST_TIME),20,BigDecimal.ROUND_HALF_UP); System.out.println("per Time cost "+bd+" ns"); } private static void test2() throws Throwable { MethodType mt = MethodType.methodType(int.class, int.class); MethodHandle m = MethodHandles.lookup().findStatic(GoodestStatic.class, "good", mt); int sum = 0; if(NEED_WARM_UP) for(int i=0; i<WARM_UP;i++){ sum += (int)m.invoke(i); } long start = System.nanoTime(); for(int i=0; i<TEST_TIME;i++){ sum += (int)m.invoke(i); } long end = System.nanoTime(); System.out.println("Result ="+sum+",Test2 cost "+(end -start)+"ns."); BigDecimal bd = new BigDecimal(end-start); bd = bd.divide(new BigDecimal(TEST_TIME),20,BigDecimal.ROUND_HALF_UP); System.out.println("per Time cost "+bd+" ns"); } private static void test3() throws Throwable { MethodType mt = MethodType.methodType(int.class, int.class); MethodHandle m = MethodHandles.lookup().findStatic(GoodestStatic.class, "good", mt); int sum = 0; if(NEED_WARM_UP) for(int i=0; i<WARM_UP;i++){ sum += (int)m.invokeExact(i); } long start = System.nanoTime(); for(int i=0; i<TEST_TIME;i++){ sum += (int)m.invokeExact(i); } long end = System.nanoTime(); System.out.println("Result ="+sum+",Test3 cost "+(end -start)+"ns."); BigDecimal bd = new BigDecimal(end-start); bd = bd.divide(new BigDecimal(TEST_TIME),20,BigDecimal.ROUND_HALF_UP); System.out.println("per Time cost "+bd+" ns"); } }
CPU: Intel [email protected]
内存: DDR3200 8Gx2
OS:win7 64bit
先使用jvm1.7进行测试:
D:\Program Files\Java\jdk1.7.0_40\bin>java -version java version "1.7.0_40" Java(TM) SE Runtime Environment (build 1.7.0_40-b43) Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode) D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic true 5000 WARM_UP =true,TEST_TIME=5000 Result =-2003482716,Test0 cost 1169ns. per Time cost 0.23380000000000000000 ns Result =-2003482716,Test1 cost 101112ns. per Time cost 20.22240000000000000000 ns Result =-2003482716,Test2 cost 392176ns. per Time cost 78.43520000000000000000 ns Result =-2003482716,Test3 cost 31269ns. per Time cost 6.25380000000000000000 ns D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic false 5000 WARM_UP =false,TEST_TIME=5000 Result =62492500,Test0 cost 98190ns. per Time cost 19.63800000000000000000 ns Result =62492500,Test1 cost 3388143ns. per Time cost 677.62860000000000000000 ns Result =62492500,Test2 cost 34774205ns. per Time cost 6954.84100000000000000000 ns Result =62492500,Test3 cost 644957ns. per Time cost 128.99140000000000000000 ns D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic true WARM_UP =true,TEST_TIME=2147483647 Result =1155250260,Test0 cost 508026604ns. per Time cost 0.23656832251537978766 ns Result =1155250260,Test1 cost 20721040125ns. per Time cost 9.64898622345597773020 ns Result =1155250260,Test2 cost 154064924123ns. per Time cost 71.74207092949285680870 ns Result =1155250260,Test3 cost 10129340931ns. per Time cost 4.71684193970488474691 ns D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic false WARM_UP =false,TEST_TIME=2147483647 Result =-1073741820,Test0 cost 515627571ns. per Time cost 0.24010779859503162959 ns Result =-1073741820,Test1 cost 7117198964ns. per Time cost 3.31420403314484471136 ns Result =-1073741820,Test2 cost 164902029058ns. per Time cost 76.78849116656393332713 ns Result =-1073741820,Test3 cost 8496807506ns. per Time cost 3.95663432309247288997 ns D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic true 5000 WARM_UP =true,TEST_TIME=5000 Result =-2003482716,Test0 cost 1169ns. per Time cost 0.23380000000000000000 ns Result =-2003482716,Test1 cost 120984ns. per Time cost 24.19680000000000000000 ns Result =-2003482716,Test2 cost 389546ns. per Time cost 77.90920000000000000000 ns Result =-2003482716,Test3 cost 34191ns. per Time cost 6.83820000000000000000 ns D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic false 5000 WARM_UP =false,TEST_TIME=5000 Result =62492500,Test0 cost 96144ns. per Time cost 19.22880000000000000000 ns Result =62492500,Test1 cost 3296090ns. per Time cost 659.21800000000000000000 ns Result =62492500,Test2 cost 35162582ns. per Time cost 7032.51640000000000000000 ns Result =62492500,Test3 cost 609304ns. per Time cost 121.86080000000000000000 ns D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic true WARM_UP =true,TEST_TIME=2147483647 Result =1155250260,Test0 cost 508109013ns. per Time cost 0.23660669719642340075 ns Result =1155250260,Test1 cost 20455020598ns. per Time cost 9.52511122800647803955 ns Result =1155250260,Test2 cost 163458260579ns. per Time cost 76.11618407774538922950 ns Result =1155250260,Test3 cost 10206268212ns. per Time cost 4.75266399642110988331 ns D:\Program Files\Java\jdk1.7.0_40\bin>java GoodestStatic false WARM_UP =false,TEST_TIME=2147483647 Result =-1073741820,Test0 cost 516316363ns. per Time cost 0.24042854236458825989 ns Result =-1073741820,Test1 cost 7059888957ns. per Time cost 3.28751698149718203652 ns Result =-1073741820,Test2 cost 171611770599ns. per Time cost 79.91295805150315074786 ns Result =-1073741820,Test3 cost 8514055068ns. per Time cost 3.96466584502005290474 ns
调用次数在测试中选用 小测试次数5000 和 大测试次数2147483647。从两次测试中可以看到,小次数调用的情况下,MethodHandle的invokeExact()方法确实具有优势。而在大次数调用下,Method的invokes执行速度变现怪异,在有预热的状况下执行速度反而处于劣势,推测因循环次数不用触发jit重编译引起,未能使用debug版测试环境验证推测。在没有预热的情况下,Method的invoke执行速度喜人,快于MethodHandle的invokeExact(),而MethodHandle的invoke()方法在jvm1.7中表现得非常差,无论小次数调用抑或大次数调用,执行速度都无法与其他两者相比。
再使用jvm1.8进行测试:
D:\Program Files\Java\jdk1.8.0_25\bin>java -version java version "1.8.0_25" Java(TM) SE Runtime Environment (build 1.8.0_25-b18) Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode) D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic true 5000 WARM_UP =true,TEST_TIME=5000 Result =-2003482716,Test0 cost 12566ns. per Time cost 2.51320000000000000000 ns Result =-2003482716,Test1 cost 98775ns. per Time cost 19.75500000000000000000 ns Result =-2003482716,Test2 cost 31561ns. per Time cost 6.31220000000000000000 ns Result =-2003482716,Test3 cost 53186ns. per Time cost 10.63720000000000000000 ns D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic false 5000 WARM_UP =false,TEST_TIME=5000 Result =62492500,Test0 cost 100528ns. per Time cost 20.10560000000000000000 ns Result =62492500,Test1 cost 2374097ns. per Time cost 474.81940000000000000000 ns Result =62492500,Test2 cost 1252508ns. per Time cost 250.50160000000000000000 ns Result =62492500,Test3 cost 432796ns. per Time cost 86.55920000000000000000 ns D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic true WARM_UP =true,TEST_TIME=2147483647 Result =1155250260,Test0 cost 513498658ns. per Time cost 0.23911644622642381407 ns Result =1155250260,Test1 cost 20213309554ns. per Time cost 9.41255575204852770644 ns Result =1155250260,Test2 cost 10503014524ns. per Time cost 4.89084726613520051638 ns Result =1155250260,Test3 cost 8798333281ns. per Time cost 4.09704320370081961327 ns D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic false WARM_UP =false,TEST_TIME=2147483647 Result =-1073741820,Test0 cost 513264286ns. per Time cost 0.23900730825914410328 ns Result =-1073741820,Test1 cost 6697535282ns. per Time cost 3.11878290265741893214 ns Result =-1073741820,Test2 cost 8808774164ns. per Time cost 4.10190511872149310947 ns Result =-1073741820,Test3 cost 8799512440ns. per Time cost 4.09759229239895580914 ns D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic true 5000 WARM_UP =true,TEST_TIME=5000 Result =-2003482716,Test0 cost 12273ns. per Time cost 2.45460000000000000000 ns Result =-2003482716,Test1 cost 100528ns. per Time cost 20.10560000000000000000 ns Result =-2003482716,Test2 cost 33315ns. per Time cost 6.66300000000000000000 ns Result =-2003482716,Test3 cost 53479ns. per Time cost 10.69580000000000000000 ns D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic false 5000 WARM_UP =false,TEST_TIME=5000 Result =62492500,Test0 cost 109003ns. per Time cost 21.80060000000000000000 ns Result =62492500,Test1 cost 2249897ns. per Time cost 449.97940000000000000000 ns Result =62492500,Test2 cost 1214518ns. per Time cost 242.90360000000000000000 ns Result =62492500,Test3 cost 413802ns. per Time cost 82.76040000000000000000 ns D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic true WARM_UP =true,TEST_TIME=2147483647 Result =1155250260,Test0 cost 515921264ns. per Time cost 0.24024456005554858598 ns Result =1155250260,Test1 cost 20449255728ns. per Time cost 9.52242675121986621582 ns Result =1155250260,Test2 cost 10516892353ns. per Time cost 4.89730963385538646665 ns Result =1155250260,Test3 cost 10538898336ns. per Time cost 4.90755696823241047945 ns D:\Program Files\Java\jdk1.8.0_25\bin>java GoodestStatic false WARM_UP =false,TEST_TIME=2147483647 Result =-1073741820,Test0 cost 516242136ns. per Time cost 0.24039397772419917291 ns Result =-1073741820,Test1 cost 7451620181ns. per Time cost 3.46993104762860156951 ns Result =-1073741820,Test2 cost 8833791018ns. per Time cost 4.11355449916494754104 ns Result =-1073741820,Test3 cost 8817718231ns. per Time cost 4.10607002447641921438 ns
从测试中可以看出,只有在小次数调用下MethodHandle才会有优势。但小次数调用的代码通常不注重什么效率,这就使人觉得其地位尴尬了。可见,Reflection的Method反射机制仍然是我们的反射首选。
附CentOS下测试,i3-4160+8G ram:
[seamas@localhost ~]$ java -version java version "1.7.0_75" OpenJDK Runtime Environment (rhel-2.5.4.2.el7_0-x86_64 u75-b13) OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode) [seamas@localhost ~]$ java GoodestStatic true 5000 WARM_UP =true,TEST_TIME=5000 Result =-2003482716,Test0 cost 2571ns. per Time cost 0.51420000000000000000 ns Result =-2003482716,Test1 cost 97881ns. per Time cost 19.57620000000000000000 ns Result =-2003482716,Test2 cost 556485ns. per Time cost 111.29700000000000000000 ns Result =-2003482716,Test3 cost 51370ns. per Time cost 10.27400000000000000000 ns [seamas@localhost ~]$ java GoodestStatic false 5000 WARM_UP =false,TEST_TIME=5000 Result =62492500,Test0 cost 677493ns. per Time cost 135.49860000000000000000 ns Result =62492500,Test1 cost 10841897ns. per Time cost 2168.37940000000000000000 ns Result =62492500,Test2 cost 60150515ns. per Time cost 12030.10300000000000000000 ns Result =62492500,Test3 cost 2022143ns. per Time cost 404.42860000000000000000 ns [seamas@localhost ~]$ java GoodestStatic true WARM_UP =true,TEST_TIME=2147483647 Result =1155250260,Test0 cost 648155339ns. per Time cost 0.30182084967467042137 ns Result =1155250260,Test1 cost 29811490738ns. per Time cost 13.88205716008416244764 ns Result =1155250260,Test2 cost 227465171362ns. per Time cost 105.92172456342807252120 ns Result =1155250260,Test3 cost 14363211021ns. per Time cost 6.68839133702609284642 ns [seamas@localhost ~]$ java GoodestStatic false WARM_UP =false,TEST_TIME=2147483647 Result =-1073741820,Test0 cost 648307322ns. per Time cost 0.30189162227413226956 ns Result =-1073741820,Test1 cost 10100713630ns. per Time cost 4.70351131386287105915 ns Result =-1073741820,Test2 cost 232768978072ns. per Time cost 108.39150202478817758373 ns Result =-1073741820,Test3 cost 12097328362ns. per Time cost 5.63325750065653468513 ns