1.基准版本
import java.lang.reflect.Method; public class ReflectionPerTest1 { public static void target(int i) { // 空方法 } public static void main(String[] args) throws Exception { Class<?> klass = Class.forName("com.enjoy.learn.core.oop.reflection.ReflectionPerTest1"); Method method = klass.getMethod("target", int.class); 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; } method.invoke(null, 128); } } }字 |
305 304 304 318 313 |
查看其字节码,可以看到有两项额外的操作:
1)128自动装箱为Integer类型
2)生成一个Object数组,并存储传入参数
这两个操作除了带来性能开销外,还可能占用堆内存,使得GC更加频繁。(-XX:+PrintGC)
public static void main(java.lang.String[]) throws java.lang.Exception; descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=6, locals=8, args_size=1 0: ldc #2 // String com.enjoy.learn.core.oop.reflection.ReflectionPerTest1 2: invokestatic #3 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; 5: astore_1 6: aload_1 7: ldc #4 // String target 9: iconst_1 10: anewarray #5 // class java/lang/Class 13: dup 14: iconst_0 15: getstatic #6 // Field java/lang/Integer.TYPE:Ljava/lang/Class; 18: aastore 19: invokevirtual #7 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; 22: astore_2 23: invokestatic #8 // Method java/lang/System.currentTimeMillis:()J 26: lstore_3 27: iconst_1 28: istore 5 30: iload 5 32: ldc #9 // int 2000000000 34: if_icmpgt 88 37: iload 5 39: ldc #10 // int 100000000 41: irem 42: ifne 63 45: invokestatic #8 // Method java/lang/System.currentTimeMillis:()J 48: lstore 6 50: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream; 53: lload 6 55: lload_3 56: lsub 57: invokevirtual #12 // Method java/io/PrintStream.println:(J)V 60: lload 6 62: lstore_3 63: aload_2 64: aconst_null 65: iconst_1 66: anewarray #13 // class java/lang/Object 69: dup 70: iconst_0 71: sipush 128 74: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 77: aastore 78: invokevirtual #15 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; 81: pop 82: iinc 5, 1 85: goto 30 88: return |
2.避免自动装箱
Integer I128 = Integer.valueOf(128); 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; } method.invoke(null, I128); } |
78: iconst_0 79: aload 5 81: aastore 82: invokevirtual #15 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; LocalVariableTable: Start Length Slot Name Signature 58 13 7 temp J 38 54 6 i I 0 93 0 args [Ljava/lang/String; 6 87 1 klass Ljava/lang/Class; 23 70 2 method Ljava/lang/reflect/Method; 27 66 3 current J 35 58 5 I128 Ljava/lang/Integer; |
192 195 190 189 190 |
Java缓存[-128,127]所有整数所定义的Integer对象,可以将范围扩大至覆盖128.
-Djava.lang.Integer.IntegerCache.high=128 78: iconst_0 79: sipush 128 82: invokestatic #9 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 85: aastore 86: invokevirtual #15 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; |
216 217 217 218 217 |
3.在循环外新建一个Object数组,避免频繁GC
但也有可能发生性能变坏,因为当对象不逃逸时,JIT可以选择栈分配甚至虚拟分配,不占用堆空间。
在堆外建立数组时,无法优化掉访问数组的操作。
Object[] argArray = new Object[1]; argArray[0] = 128; 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; } method.invoke(null, argArray); } |
245 241 243 244 243 |
4.关闭反射调用的Inflation机制和权限检查
从而取消委派实现,直接使用动态实现。
此外,每次反射调用都会检查目标方法的权限,关闭掉。
-Dsun.reflect.noInflation=true |
method.setAccessible(true); // 关闭权限检查 Integer I128 = Integer.valueOf(128); 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; } method.invoke(null, I128); } |
123 124 123 125 122 |
5.内联瓶颈——Method.invoke
变快的原因是JIT中的方法内联。在关闭Inflation的情况下,内联的瓶颈在于Method.invoke方法中对MethodAccessor.invoke方法的调用。
类型profile:对于invokevirtula或者invokeinterface,JVM会记录下调用者的具体类型。
public class ReflectionPerTest1 { public static void target(int i) { // 空方法 } public static void main(String[] args) throws Exception { Class<?> klass = Class.forName("com.enjoy.learn.core.oop.reflection.ReflectionPerTest1"); Method method = klass.getMethod("target", int.class); method.setAccessible(true); // 关闭权限检查 polluteProfile(); long current = System.currentTimeMillis(); Integer I128 = Integer.valueOf(128); 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; } method.invoke(null, 128); } } public static void polluteProfile() throws Exception { Method method1 = ReflectionPerTest1.class.getMethod("target1", int.class); Method method2 = ReflectionPerTest1.class.getMethod("target2", int.class); for (int i = 0; i < 2000; i++) { method1.invoke(null, 0); method2.invoke(null, 0); } } public static void target1(int i) { } public static void target2(int i) { } } |
1047 1024 1021 1030 1042 |
只要干扰了Method.invoke方法的类型profile,性能开销会急剧上升。
提高JVM关于每个调用能够记录的类型数目(-xx:TypeProfileWidth=3默认值为2)。测试结果无变化。
6.多次获取的Method是同一个对象
public static void polluteProfile() throws Exception { Method method1 = ReflectionPerTest1.class.getMethod("target", int.class); Method method2 = ReflectionPerTest1.class.getMethod("target", int.class); System.out.println(method1.equals(method2)); for (int i = 0; i < 2000; i++) { method1.invoke(null, 0); method2.invoke(null, 0); } } |
true ... 300 302 309 307 305 |
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理