[Loaded sun.reflect.NativeMethodAccessorImpl from /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/rt.jar] [Loaded sun.reflect.DelegatingMethodAccessorImpl from /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/rt.jar] java.lang.RuntimeException at com.air.lang.reflect.ReflectTest.getCount(ReflectTest.java:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.air.lang.reflect.ReflectTest.testReflection(ReflectTest.java:82) // 下面是junit用反射调用这个方法的栈 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68) o = 10
// java.lang.reflect.Method#invoke @CallerSensitive public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor; // read volatile if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); }
最终调用是委托给了MethodAccessor,这是java中的一个接口:
1 2 3 4 5 6 7 8 9 10 11 12 13
// sun.reflect.MethodAccessor
/** This interface provides the declaration for java.lang.reflect.Method.invoke(). Each Method object is configured with a (possibly dynamically-generated) class which implements this interface. */
publicinterfaceMethodAccessor{ /** Matches specification in {@link java.lang.reflect.Method} */ public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException; }
Before Java 1.4 Method.invoke worked through a JNI call to VM runtime.
Since Java 1.4 Method.invoke uses dynamic bytecode generation if a method is called more than 15 times (configurable via sun.reflect.inflationThreshold system property).
// sun.reflect.NativeMethodAccessorImpl#invoke public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { // We can't inflate methods belonging to vm-anonymous classes because // that kind of class can't be referred to by name, hence can't be // found from the generated bytecode. if (++numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { MethodAccessorImpl acc = (MethodAccessorImpl) // 超过阈值之后,会切换成动态字节码的方式 // 注意,这里没有加锁 new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers()); // parent就是刚才说的代理DelegatingMethodAccessorImpl // 生成结束之后,这里切换成新的调用方式 parent.setDelegate(acc); }
The approach with dynamic bytecode generation is much faster since it
does not suffer from JNI overhead;
does not need to parse method signature each time, because each method invoked via Reflection has its own unique MethodAccessor;
can be further optimized, e.g. these MethodAccessors can benefit from all regular JIT optimizations like inlining, constant propagation, autoboxing elimination etc.
Note, that this optimization is implemented mostly in Java code without JVM assistance. The only thing HotSpot VM does to make this optimization possible - is skipping bytecode verification for such generated MethodAccessors. Otherwise the verifier would not allow, for example, to call private methods.
稍微改造下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
@Test @SneakyThrows publicvoidtestReflection(){ Class<?> clazz = Class.forName("com.air.lang.reflect.ReflectTest"); Method getCountMethod = clazz.getDeclaredMethod("getCount", null); final Object instance = clazz.newInstance(); for (int i = 0; i < 20; i++) { final Object o = getCountMethod.invoke(instance); System.out.println("o = " + o); } // 阻塞退出,等待输入 System.in.read(); }
程序跑起来之后,反复调用了20次,超过了默认的阈值,会自动生成字节码。
第一次输出的调用栈:
1 2 3 4 5 6 7
java.lang.RuntimeException at com.air.lang.reflect.ReflectTest.getCount(ReflectTest.java:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.air.lang.reflect.ReflectTest.testReflection(ReflectTest.java:83)
最后一次输出的调用栈:
1 2 3 4 5 6
java.lang.RuntimeException at com.air.lang.reflect.ReflectTest.getCount(ReflectTest.java:21) at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.air.lang.reflect.ReflectTest.testReflection(ReflectTest.java:83)
// sun.reflect.DelegatingClassLoader // NOTE: this class's name and presence are known to the virtual // machine as of the fix for 4474172. classDelegatingClassLoaderextendsClassLoader{ DelegatingClassLoader(ClassLoader parent) { super(parent); } }
// jdk.internal.reflect.ClassDefiner /** Utility class which assists in calling defineClass() by creating a new class loader which delegates to the one needed in order for proper resolution of the given bytecodes to occur. */
/** <P> We define generated code into a new class loader which delegates to the defining loader of the target class. It is necessary for the VM to be able to resolve references to the target class from the generated bytecodes, which could not occur if the generated code was loaded into the bootstrap class loader. </P> <P> There are two primary reasons for creating a new loader instead of defining these bytecodes directly into the defining loader of the target class: first, it avoids any possible security risk of having these bytecodes in the same loader. Second, it allows the generated bytecodes to be unloaded earlier than would otherwise be possible, decreasing run-time footprint. </P> */ static Class<?> defineClass(String name, byte[] bytes, int off, int len, final ClassLoader parentClassLoader) { ClassLoader newLoader = AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { public ClassLoader run(){ returnnew DelegatingClassLoader(parentClassLoader); } }); return JLA.defineClass(newLoader, name, bytes, null, "__ClassDefiner__"); } }