JVM原理解读——反射
以一个简单的例子开始:
public class User {
private Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
User user = new User();
Class<?> kClass = Class.forName("com.company.User");
Method method = kClass.getMethod("setAge", Integer.class);
for (int i=0;i<15;i++){
method.invoke(user,i);
}
1、Class.forName——获取类
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
//获取调用者,是个native方法
Class<?> caller = Reflection.getCallerClass();
//使用调用者的类加载器寻找目标类,也是native方法
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
2、getMethod——获取目标方法
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
//跟SystemSecurity有关,一般都是空,跳过
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
//获取类的所有方法
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
获取类的所有方法调用getMethod0
private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
MethodArray interfaceCandidates = new MethodArray(2);
Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
if (res != null)
return res;
// Not found on class or superclass directly
interfaceCandidates.removeLessSpecifics();
return interfaceCandidates.getFirst(); // may be null
}
private Method privateGetMethodRecursive(String name,
Class<?>[] parameterTypes,
boolean includeStaticMethods,
MethodArray allInterfaceCandidates) {
Method res
//获取类的类的所有公有方法
if ((res = searchMethods(privateGetDeclaredMethods(true),
name,
parameterTypes)) != null) {
if (includeStaticMethods || !Modifier.isStatic(res.getModifiers()))
return res;
}
//找父类
if (!isInterface()) {
Class<? super T> c = getSuperclass();
if (c != null) {
if ((res = c.getMethod0(name, parameterTypes, true)) != null) {
return res;
}
}
}
//找接口
Class<?>[] interfaces = getInterfaces();
for (Class<?> c : interfaces)
if ((res = c.getMethod0(name, parameterTypes, false)) != null)
allInterfaceCandidates.add(res);
// Not found
return null;
}
获取类的所有方法调用privateGetDeclaredMethods
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
}
//getDeclaredMethods0才是真正获取类的所有方法,之前的不过都是嵌套加细节处理
//然后通过反射过滤获取匹配的方法,
//这里我们就获取到了setAge方法
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
}
return res;
}
3、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
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
//通过MethodAccessor执行invoke
return ma.invoke(obj, args);
}
初始化的是DelegatingMethodAccessorImpl,调用它的invoke方法
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
return this.delegate.invoke(var1, var2);
}
是NativeMethodAccessorImpl中invoke方法的封装,这样的实现也叫本地实现
//该方法通过本地实现的次数,当达到阈值,会将实现转化为动态字节码实现
private int numInvocations;
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
//inflationThreshold获取反射调用的域值,默认为15,可以通过-Dsun.reflect.inflationThreshold=调整
//当反射调用次数确实很频繁,可以通过-Dsun.reflect.noInflation=true从第一次调用就直接使用字节码实现
//从语义上看匿名类是无法通过字节码实现的
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);
}
//执行方法调用
//本地实现是调用native方法
//字节码实现是解析内存中对应方法的字节码
return invoke0(this.method, var1, var2);
}
生成对应方法的字节码调用generateMethod,是generate方法的封装
public MethodAccessor generateMethod(Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6) {
return (MethodAccessor)this.generate(var1, var2, var3, var4, var5, var6, false, false, (Class)null);
}
private MagicAccessorImpl generate(final Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6, boolean var7, boolean var8, Class<?> var9) {
//asm是java的字节码技术,底层是使用ByteVector将字节码放在内存中
ByteVector var10 = ByteVectorFactory.create();
this.asm = new ClassFileAssembler(var10);
this.declaringClass = var1;
this.parameterTypes = var3;
this.returnType = var4;
this.modifiers = var6;
this.isConstructor = var7;
this.forSerialization = var8;
//都是进行字节码操作,挑一个简单的看
this.asm.emitMagicAndVersion();
//以下省略...
}
调用emitMagicAndVersion往ByteVector里写字节码
public void emitMagicAndVersion() {
//-889275714换算成16进制就是cafebabe,是java文件的标识
this.emitInt(-889275714);
//asm生成的字节码好像把版本固定到1.5了
this.emitShort((short)0);
this.emitShort((short)49);
}
4、方法内联的失效
JVM记录每个反射调用方法的类型是有限的,靠后调用的方法会导致JVM优化方法内联失败,增加栈帧出入的消耗。可以通过-XX:TypeProfileWidth=调整,默认值是2。我并没有找到profile相关的源码,如果有读者接触过希望可以互相分享