Method.invoke()用途广泛
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import java.lang.reflect.Method; public class MethodTest { public static void main(String[] args) throws Exception { Class<?> clz = Class.forName("com.method.TestClassLoad"); Object o = clz.newInstance(); Method m = clz.getMethod("test", String.class,Integer.class); for (int i = 0; i < 16; i++) { m.invoke(o, "huige",i); } } } class TestClassLoad { public void test(String s,Integer i){ System.out.println(s+":测试:"+i); } }
注意到MethodTest 类上不会有对类TestClassLoad 的符号依赖——也就是说在加载并初始化MethodTest 类时不需要关心类TestClassLoad 的存在与否,而是等到main()方法执行到调用Class.forName()时才试图对类TestClassLoad 做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的ClassLoader来加载,并且初始化新加载的类
原理
JDK里Method.invoke()是怎么实现的。
java.lang.reflect.Method#invoke
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44@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();//获取MethodAccessor } return ma.invoke(obj, args); } private Method root; //获取MethodAccessor private MethodAccessor acquireMethodAccessor() { // First check to see if one has been created yet, and take it // if so MethodAccessor tmp = null; if (root != null) tmp = root.getMethodAccessor(); if (tmp != null) { methodAccessor = tmp; } else { // Otherwise fabricate one and propagate it up to the root tmp = reflectionFactory.newMethodAccessor(this); setMethodAccessor(tmp); } return tmp; } //把新创建methodAccessor对象通过root包装起来 void setMethodAccessor(MethodAccessor accessor) { methodAccessor = accessor; // Propagate up if (root != null) { root.setMethodAccessor(accessor); } }
可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。 每个实际的Java方法只有一个对应的Method对象作为root。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时,把新创建methodAccessor对象通过root包装起来。在第一次调用一个实际Java方法对应的Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。
那么MethodAccessor是啥呢?
sun.reflect.MethodAccessor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50public interface MethodAccessor { Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException; } 可以看到它只是一个单方法接口,其invoke()方法与Method.invoke()的对应。 创建MethodAccessor实例的是ReflectionFactory。 sun.reflect.ReflectionFactory#newMethodAccessor private static boolean noInflation = false; //调用超过15次就采用java版本 private static int inflationThreshold = 15; private static void checkInitted() { if (!initted) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { if (System.out == null) { return null; } else { String var1 = System.getProperty("sun.reflect.noInflation"); if (var1 != null && var1.equals("true")) { ReflectionFactory.noInflation = true; } var1 = System.getProperty("sun.reflect.inflationThreshold"); if (var1 != null) { try { ReflectionFactory.inflationThreshold = Integer.parseInt(var1); } catch (NumberFormatException var3) { throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", var3); } } ReflectionFactory.initted = true; return null; } } }); } } public MethodAccessor newMethodAccessor(Method var1) { checkInitted(); if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) { return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers()); } else { NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1); DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2); var2.setParent(var3); return var3; }
从上面代码可以看出:实际的MethodAccessor实现有两个版本
(1)一个是Java实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;
(2)另一个是native code实现的。native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了权衡两个版本的性能,Sun的JDK使用了inflation的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值(15次)时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。
验证下:
编译上述代码,并在执行MethodTest 时加入-XX:+TraceClassLoading参数(或者-verbose:class或者直接-verbose都行),如下:
java -XX:+TraceClassLoading MethodTest
如果用IDEA,可以通过
点击Edit Configurations配置
image
可以看到输出了一大堆log,把其中相关的部分截取出来如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31huige:测试:0 huige:测试:1 huige:测试:2 huige:测试:3 huige:测试:4 huige:测试:5 huige:测试:6 huige:测试:7 huige:测试:8 huige:测试:9 huige:测试:10 huige:测试:11 huige:测试:12 huige:测试:13 huige:测试:14 [Loaded sun.reflect.ClassFileConstants from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.AccessorGenerator from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.MethodAccessorGenerator from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.ByteVectorFactory from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.ByteVector from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.ByteVectorImpl from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.ClassFileAssembler from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.UTF8 from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.Label from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.Label$PatchInfo from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.MethodAccessorGenerator$1 from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.ClassDefiner from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.ClassDefiner$1 from D:projectJavajdk1.8.0_101jrelibrt.jar] [Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__] huige:测试:15
可以看到在执行16次时被触发了,导致JVM新加载了一堆类,其中就包括[Loaded sun.reflect.GeneratedMethodAccessor1 from JVM_DefineClass]这么一行
具体实现如下:
(1)MethodAccessor实现版本:开头若干次使用native版
通过DelegatingMethodAccessorImpl实现的,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43```java class DelegatingMethodAccessorImpl extends MethodAccessorImpl { private MethodAccessorImpl delegate; DelegatingMethodAccessorImpl(MethodAccessorImpl var1) { this.setDelegate(var1); } public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { return this.delegate.invoke(var1, var2); } //var1就是NativeMethodAccessorImpl var1 = new NativeMethodAccessorImpl(var1); void setDelegate(MethodAccessorImpl var1) { this.delegate = var1; } 当Method.invoke()调用时,同时调用sun.reflect.DelegatingMethodAccessorImpl#invoke方法,即调用sun.reflect.NativeMethodAccessorImpl#invoke方法,代码如下: class NativeMethodAccessorImpl extends MethodAccessorImpl { private final Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; NativeMethodAccessorImpl(Method var1) { this.method = var1; } public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) { MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers()); this.parent.setDelegate(var3); } return invoke0(this.method, var1, var2); } void setParent(DelegatingMethodAccessorImpl var1) { this.parent = var1; } private static native Object invoke0(Method var0, Object var1, Object[] var2); }
从上面代码可以看出:每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器numInvocations,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。
注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0 (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args) { return JVM_InvokeMethod(env, m, obj, args); } JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0)) JVMWrapper("JVM_InvokeMethod"); Handle method_handle; if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) { method_handle = Handle(THREAD, JNIHandles::resolve(method)); Handle receiver(THREAD, JNIHandles::resolve(obj)); objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0))); oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL); jobject res = JNIHandles::make_local(env, result); if (JvmtiExport::should_post_vm_object_alloc()) { oop ret_type = java_lang_reflect_Method::return_type(method_handle()); assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!"); if (java_lang_Class::is_primitive(ret_type)) { // Only for primitive type vm allocates memory for java object. // See box() method. JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } } return res; } else { THROW_0(vmSymbols::java_lang_StackOverflowError()); } JVM_END 其中的关键又是Reflection::invoke_method(): // This would be nicer if, say, java.lang.reflect.Method was a subclass // of java.lang.reflect.Constructor oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) { oop mirror = java_lang_reflect_Method::clazz(method_mirror); int slot = java_lang_reflect_Method::slot(method_mirror); bool override = java_lang_reflect_Method::override(method_mirror) != 0; objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror))); oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror); BasicType rtype; if (java_lang_Class::is_primitive(return_type_mirror)) { rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL); } else { rtype = T_OBJECT; } instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(mirror)); methodOop m = klass->method_with_idnum(slot); if (m == NULL) { THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke"); } methodHandle method(THREAD, m); return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD); }
(2)MethodAccessor实现版本:java版本MethodAccessorGenerator
MethodAccessorGenerato如何实现的呢?
具体实现在sun.reflect.MethodAccessorGenerator#generate方法
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private MagicAccessorImpl generate(final Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6, boolean var7, boolean var8, Class<?> var9) { //...... //这里生成[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__] final String var13 = generateName(var7, var8); //。。。。。。 this.targetMethodRef = this.asm.cpi(); if (var7) { //构建newInstance this.asm.emitConstantPoolUTF8("newInstance"); } else { //构建invoke this.asm.emitConstantPoolUTF8("invoke"); } //......... }
最后生成的Java版MethodAccessor大致如下
public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
public GeneratedMethodAccessor1() {
super();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { // prepare the target and parameters if (obj == null) throw new NullPointerException(); try { TestClassLoad target = (TestClassLoad) obj; String arg0 = (String) args[0]; Integer arg1 = (Integer ) args[1]; } catch (ClassCastException e) { throw new IllegalArgumentException(e.toString()); } catch (NullPointerException e) { throw new IllegalArgumentException(e.toString()); } // 调用目标方法 try { target.test(arg0,arg1 ); } catch (Throwable t) { throw new InvocationTargetException(t); } }
}
1
2
3
4
5
6
7总结 就反射调用而言,这个invoke()方法非常干净(然而就“正常调用”而言这额外开销还是明显的)。注意到参数数组被拆开了,把每个参数都恢复到原本没有被Object[]包装前的样子,然后对目标方法做正常的invokevirtual调用。由于在生成代码时已经循环遍历过参数类型的数组,生成出来的代码里就不再包含循环了。 当该反射调用成为热点时,它甚至可以被内联到靠近Method.invoke()的一侧,大大降低了反射调用的开销。而native版的反射调用则无法被有效内联,因而调用开销无法随程序的运行而降低。 虽说Sun的JDK这种实现方式使得反射调用方法成本比以前降低了很多,但Method.invoke()本身要用数组包装参数;而且每次调用都必须检查方法的可见性(在Method.invoke()里),也必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0()里或者生成的Java版MethodAccessor.invoke()里);而且Method.invoke()就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得Method.invoke()自身难以被内联到调用方。 相比之下MethodHandle则更有潜力,在其功能完全实现后能达到比普通反射调用方法更高的性能。在使用MethodHandle来做反射调用时,MethodHandle.invoke()的形式参数与返回值类型都是准确的,所以只需要在链接方法的时候才需要检查类型的匹配性,而不必在每次调用时都检查。而且MethodHandle是不可变值,在创建后其内部状态就不会再改变了;JVM可以利用这个知识而放心的对它做激进优化,例如将实际的调用目标内联到做反射调用的一侧
最后
以上就是笑点低凉面最近收集整理的关于JDK1.8里Method.invoke()的实现原理的全部内容,更多相关JDK1.8里Method.invoke()内容请搜索靠谱客的其他文章。
发表评论 取消回复