Method.invoke实际上是委派给MethodAccessor来实现,它有两种具体实现,一个通过本地方法来实现反射调用,一个则使用了委派模式
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> klass = Class.forName("Test");
//每个Method实例的第一次反射调用都会生成一个委派实现,它所委派的具体实现便是一个本地实现。
Method method = klass.getMethod("target", int.class);
method.invoke(null, 0);
}
public static void target(int i) {
//我们打印反射调用的栈轨迹
new Exception("#" + i).printStackTrace();
}
}
java.lang.Exception: #0
at Test.target(Test.java:12)
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 Test.main(Test.java:8)
调用过程:Method.invoke->DelegatingMethodAccessorImpl->NativeMethodAccessorlmpl->本地方法
为什么
为什么反射调用还要采取委派实现作为中间层 (Inflation过程)
其实,java的反射调用机制还设立了另一种动态生成字节码的实现(动态实现),直接使用invoke指令来调用目标方法。之所以采用委派实现,便是为了能够在本地实现以及动态实现中切换,动态实现比本地实现快20倍,这是因为动态实现无需经过java到c++到java的切换,但由于生成字节码比较耗时,仅调用一次的话,还是本地实现快上3到4倍
解决思路
设立阈值:Dsun.reflect.inflation.ThresHold = 15
if >= 15 委派给动态实现 (从0开始数)
if < 15 委派给本地实现
java.lang.Exception: #15
at Test.target(Test.java:17)
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 Test.main(Test.java:11)
java.lang.Exception: #16
at Test.target(Test.java:17)
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 Test.main(Test.java:11)
Inflation过程可通过参数:-Dsun.reflect.noInflation=true关闭,这样就不会使用委派模式和本地实现,直接动态实现
网友评论