1. 前言
为了分析 Java 反射导致 Metaspace OOM 的问题,对通过反射调用方法的源码进行了分析
2. 相关源码地址
https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/java/lang/reflect/Method.java
https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/java/lang/reflect/Constructor.java
https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/sun/reflect/ReflectionFactory.java
https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/sun/reflect/misc/ReflectUtil.java
https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/sun/reflect/NativeMethodAccessorImpl.java
https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/sun/reflect/DelegatingMethodAccessorImpl.java
https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/sun/reflect/MethodAccessorGenerator.java
https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/native/sun/reflect/NativeAccessors.c
https://github.com/openjdk/jdk/blob/jdk8-b120/hotspot/src/share/vm/prims/jvm.cpp
ReflectionFactory 类的源码中有有关于 Java 反射膨胀(Inflation)机制的说明
3. 反射调用非构造函数方法代码分析
3.1. Method.invoke
通过反射调用非构造函数方法时,需要执行 java.lang.reflect.Method invoke 方法,代码如下
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 ma 赋值为字段 methodAccessor,若为 null,则调用 acquireMethodAccessor 方法,并将返回值保存到变量 ma
最后调用变量 ma 的 invoke 方法,并返回对应返回值
若字段 methodAccessor 为 null,则最后调用的是 acquireMethodAccessor 方法返回值的 invoke 方法
若字段 methodAccessor 非 null,则最后调用的是 methodAccessor 字段的 invoke 方法
3.1.1. Method.acquireMethodAccessor
以上方法会调用 java.lang.reflect.Method acquireMethodAccessor 方法,代码如下
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;
}
acquireMethodAccessor 方法注释有说明,该方法没有进行并发控制,对一个方法生成多个 MethodAccessor 是正确的,避免进行同步控制可能会使实现更具有可扩展性。
root 字段只有在 java.lang.reflect.Method copy 方法中会被赋值为 this,其他情况下都是 null,因此需要调用 sun.reflect.ReflectionFactory newMethodAccessor 方法
sun.reflect.ReflectionFactory newMethodAccessor 方法的返回值会通过 setMethodAccessor 方法赋值到 methodAccessor 字段中,再返回该值
3.2. ReflectionFactory.newMethodAccessor
java.lang.reflect.Method acquireMethodAccessor 方法会调用 sun.reflect.ReflectionFactory newMethodAccessor 方法,newMethodAccessor 方法代码如下:
public MethodAccessor newMethodAccessor(Method method) {
checkInitted();
if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
return new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
} else {
NativeMethodAccessorImpl acc =
new NativeMethodAccessorImpl(method);
DelegatingMethodAccessorImpl res =
new DelegatingMethodAccessorImpl(acc);
acc.setParent(res);
return res;
}
}
若 noInflation 字段为 true,且 ReflectUtil.isVMAnonymousClass 方法返回 false,会调用 sun.reflect.MethodAccessorGenerator generateMethod 方法,为通过反射调用的方法生成类
若不满足以上条件,则分别创建 NativeMethodAccessorImpl、DelegatingMethodAccessorImpl 对象,返回 DelegatingMethodAccessorImpl 对象
若 JVM 参数 sun.reflect.noInflation 设置为 true,通过反射调用某方法时,第一次就会为对应方法生成类
3.3. ReflectUtil.isVMAnonymousClass
ReflectUtil.isVMAnonymousClass 方法用于判断指定的类是否为 VM 匿名类(与 Java 匿名类不同),在指定的类名中包含"/"时会返回 true
使用“jmap -histo {pid}”命令查看某个 Java 进程当前的类与实例信息,可以看到类名包含"/"的类,即 VM 匿名类,如下所示:
num #instances #bytes class name
----------------------------------------------
128: 1118 35776 org.springframework.util.ConcurrentReferenceHashMap$Segment$$Lambda$75/1682514668
172: 1022 24528 org.reflections.scanners.Scanner$$Lambda$262/944164860
173: 1019 24456 org.reflections.util.QueryBuilder$$Lambda$311/1967581772
207: 1023 16368 java.util.stream.Collectors$$Lambda$280/2053777753
209: 1022 16352 org.reflections.Reflections$$Lambda$261/414922522
210: 1022 16352 org.reflections.scanners.Scanners$$Lambda$263/723106091
普通的类名中不会包含"/",不属于 VM 匿名类,因此以上 sun.reflect.ReflectionFactory newMethodAccessor 方法中是否会调用 sun.reflect.MethodAccessorGenerator generateMethod 方法为通过反射调用的方法生成类,取决于 noInflation 字段值
3.4. ReflectionFactory.checkInitted
在 sun.reflect.ReflectionFactory checkInitted 方法中
读取 JVM 参数 sun.reflect.noInflation,若非空且值为"true",则将 sun.reflect.ReflectionFactory 类的字段 noInflation 赋值为 true,该字段默认值为 false
读取 JVM 参数 sun.reflect.inflationThreshold,若非空且为合法的整形数值,则将 sun.reflect.ReflectionFactory 类的字段 inflationThreshold 赋值为对应值,该字段默认值为 15
3.5. MethodAccessorGenerator.generateMethod
在 sun.reflect.MethodAccessorGenerator generateMethod 方法中,会调用 generate 方法
在 sun.reflect.MethodAccessorGenerator generate 方法中,会动态生成类的字节码,使用了相同的结构,会执行通过反射调用的方法,因此反射膨胀机制为不同的方法生成类时,生成的类大小应该很接近,每个类占用 Metaspace 大小也应该接近
generate 方法中调用 generateName 获得生成类的类名,generateName 方法代码如下
private static synchronized String generateName(boolean isConstructor,
boolean forSerialization)
{
if (isConstructor) {
if (forSerialization) {
int num = ++serializationConstructorSymnum;
return "sun/reflect/GeneratedSerializationConstructorAccessor" + num;
} else {
int num = ++constructorSymnum;
return "sun/reflect/GeneratedConstructorAccessor" + num;
}
} else {
int num = ++methodSymnum;
return "sun/reflect/GeneratedMethodAccessor" + num;
}
}
可以看到为通过反射调用的方法生成的 sun.reflect.GeneratedSerializationConstructorAccessor…、sun.reflect.GeneratedConstructorAccessor…、sun.reflect.GeneratedMethodAccessor… 类的类名前缀
3.6. DelegatingMethodAccessorImpl
sun.reflect.DelegatingMethodAccessorImpl 类的代码如下
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
private MethodAccessorImpl delegate;
DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
setDelegate(delegate);
}
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
return delegate.invoke(obj, args);
}
void setDelegate(MethodAccessorImpl delegate) {
this.delegate = delegate;
}
}
在构造函数中,会调用 setDelegate 方法,参数 1 使用构造函数的参数 1
在 setDelegate 方法中,会将 delegate 字段赋值为参数 1
在 invoke 方法中,会调用 delegate 字段的 invoke 方法
即调用 DelegatingMethodAccessorImpl 构造函数时传入的对象,会在 invoke 方法中调用该对象的 invoke 方法
3.7. NativeMethodAccessorImpl
sun.reflect.NativeMethodAccessorImpl 类的代码如下
class NativeMethodAccessorImpl extends MethodAccessorImpl {
private Method method;
private DelegatingMethodAccessorImpl parent;
private int numInvocations;
NativeMethodAccessorImpl(Method method) {
this.method = method;
}
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.setDelegate(acc);
}
return invoke0(method, obj, args);
}
void setParent(DelegatingMethodAccessorImpl parent) {
this.parent = parent;
}
private static native Object invoke0(Method m, Object obj, Object[] args);
}
3.7.1. 构造函数
在构造函数中,会将 method 字段赋值为参数 1
3.7.2. invoke 方法
在 invoke 方法中,若满足 ++numInvocations > ReflectionFactory.inflationThreshold() 且 !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass()),会执行以下处理:
- 调用 sun.reflect.MethodAccessorGenerator generateMethod 方法为当前通过反射调用的方法生成类
- 调用 parent.setDelegate,即 sun.reflect.DelegatingMethodAccessorImpl setDelegate 方法,将 DelegatingMethodAccessorImpl 的 delegate 字段赋值为刚生成的类
- 调用 native 方法 invoke0
numInvocations 代表当前方法通过反射调用的次数,ReflectionFactory.inflationThreshold() 方法返回 ReflectionFactory 类的 inflationThreshold 字段,由 JVM 参数 sun.reflect.inflationThreshold 决定
ReflectUtil.isVMAnonymousClass 方法已分析过,普通类不会满足该方法
3.7.3. 总结
当前方法通过反射调用次数大于 JVM 参数 sun.reflect.inflationThreshold 值时,会为对应的方法生成类,并将该类赋值到对应的 DelegatingMethodAccessorImpl 类的 delegate 字段;即后续都会调用生成的类的方法
3.8. NativeAccessors.c
在 NativeAccessors.c 中定义了 Java 中 sun.reflect.NativeMethodAccessorImpl 类的 native 方法 invoke0,会调用 JVM_InvokeMethod 方法:
JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0
(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)
{
return JVM_InvokeMethod(env, m, obj, args);
}
3.9. jvm.h
JVM_InvokeMethod 方法定义在 jvm.h 中
/*
* java.lang.reflect.Method
*/
JNIEXPORT jobject JNICALL
JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0);
3.10. jvm.cpp
JVM_InvokeMethod 方法在 jvm.cpp 中实现,会执行指定的 method,代码略
4. 反射调用构造函数代码分析
反射调用构造函数,与调用非构造函数的过程类似,具体步骤不再分析,以下为相对应的方法
| 反射调用构造函数时执行的类或方法 | 反射调用非构造函数方法时执行的类或方法 |
|---|---|
| java.lang.reflect.Constructor newInstance | java.lang.reflect.Method invoke |
| java.lang.reflect.Constructor acquireConstructorAccessor | java.lang.reflect.Method acquireMethodAccessor |
| sun.reflect.ReflectionFactory newConstructorAccessor | sun.reflect.ReflectionFactory newMethodAccessor |
| sun.reflect.MethodAccessorGenerator generateConstructor | sun.reflect.MethodAccessorGenerator generateMethod |
| sun.reflect.NativeConstructorAccessorImpl | sun.reflect.NativeMethodAccessorImpl |
| sun.reflect.DelegatingConstructorAccessorImpl | sun.reflect.DelegatingMethodAccessorImpl |
| sun.reflect.NativeConstructorAccessorImpl newInstance | sun.reflect.NativeMethodAccessorImpl invoke |
1693

被折叠的 条评论
为什么被折叠?



