概述
接下来咱们从源码角度来探索Java反射实现的原理。
public class ReflectionTest {
private static int count=0;
public static void foo(){
new Exception((++count)+"次").printStackTrace();
}
public static void main(String[] args) throws Exception {
Class<?> clz= Class.forName("ReflectionTest");
Method method=clz.getMethod("foo");
for(int i=0;i<20;i++){
method.invoke(null);
}
}
}
对于下面的堆栈执行结果我们可以看到从第17次开始,函数调用方式出现了变化,
java.lang.Exception: 16次
at com.Reflection.foo(Reflection.java:13)
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.Reflection.main(Reflection.java:20)
java.lang.Exception: 17次
at com.Reflection.foo(Reflection.java:13)
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.Reflection.main(Reflection.java:20)
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的invoke函数,我们可以看到MethodAccessor是一个接口,实现代码如下:
public interface MethodAccessor {
Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException;
}
我们可以看到MethodAccessor的实现类有3个,分别为DelegatingMethodAccessorImpl、MethodAccessorImpl以及NativeMethodAccessorImpl。通过堆栈我们可以看到在前16次调用的实现类NativeMethodAccessorImpl,之后调用的为GeneratedMethodAccessor1,那么接下来就让我们来探索一下NativeMethodAccessorImpl实现类。
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);
}
我们可以看到在进行方法调用的过程中存在一个条件判断 吧吧
++this.numInvocations > ReflectionFactory.inflationThreshold()
当在这个阈值之内的时候会调用invoke0方法,这是一个native函数,当超过这个阈值之后生成MethodAccessorGenerator对象之后通过ASM生成新的类sun.reflect.GeneratedMethodAccessor1,可以使用arthas工具查看生成的类的字节码,对字节码翻译之后的代码如下:
public class GeneratedMethodAccessor1 extends MethodAccessorImpl{
@Override
public Object invoke(Object obj,Object[] args) throws IllegalArgumentException,InvocationTargetException{
ReflectTest.foo();
return null;
}
}
那么问题来了,为什么要设置一个阈值来进行两种方式的调用呢。其实这是基于性能的考虑,JNI native调用的方式要比动态生成类调用的方式慢20倍左右,但是由于第一次字节码生成的过程是比较慢的,如果反射仅调用一次的话,采用生成字节码的方式反而比native调用的方式慢了3-4倍。因此Java引入了inflation机制,当调用次数小于阈值时,采用native方法,没有额外类的生成、校验以及加载的开销;当超过阈值时,会使用ASM生成新类,保证后面的调用比native要快。
参考书籍:
王磊. Offer来了[M].北京:电子工业出版社,2020.
张亚. 深入理解JVM字节码[M].北京:机械工业出版社,2020.
更多信息关注微信公众号: IT技术发展之路
最后
以上就是冷傲金毛为你收集整理的Java反射原理分析的全部内容,希望文章能够帮你解决Java反射原理分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复