[Recommandations d'apprentissage associées : Tutoriel de base Java]
En parlant de Java Quand on débute En contact avec les différentes fonctionnalités avancées de la réflexion, ils expriment souvent une grande excitation et utilisent même avec force la réflexion pour « montrer leurs compétences » dans certaines scènes qui ne nécessitent pas l'utilisation de la réflexion. Les personnes âgées plus expérimentées se posent souvent trois questions dans leur âme lorsqu'elles voient la réflexion : Pourquoi utiliser la réflexion ? La réflexion ne réduit-elle pas les performances ? Existe-t-il un autre moyen de résoudre ce problème ?
Alors aujourd'hui, nous allons discuter en profondeur de l'impact de la réflexion sur la performance ? Au fait, explorons pourquoi la réflexion affecte les performances ?
Avant d'analyser les principes spécifiques, nous pouvons tirer des conclusions en écrivant du code pour faire des expériences.
La réflexion peut impliquer de nombreux types d'opérations, telles que la génération d'instances, l'obtention/définition d'attributs de variables, l'appel de méthodes, etc. Après une simple réflexion, nous pensons que la génération d'instances a un plus grand impact sur les performances que d'autres opérations, nous utilisons donc la génération d'instances à des fins d'expérimentation.
Dans le code suivant, nous définissons une classe InnerClass
Nous testons en utilisant respectivement new
et 反射
pour générer MAX_TIMES
et imprimer le temps écoulé.
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainAc"; private final int MAX_TIMES = 100 * 1000; private InnerClass innerList[]; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); innerList = new InnerClass[MAX_TIMES]; long startTime = SystemClock.elapsedRealtime(); for (int i=0; i < MAX_TIMES; i++) { innerList[i] = new InnerClass(); } Log.e(TAG, "totalTime: " + (SystemClock.elapsedRealtime() - startTime)); long startTime2 = SystemClock.elapsedRealtime(); for (int i=0; i < MAX_TIMES; i++) { innerList[i] = newInstanceByReflection(); } Log.e(TAG, "totalTime2: " + (SystemClock.elapsedRealtime() - startTime2)); } public InnerClass newInstanceByReflection() { Class clazz = InnerClass.class; try { return (InnerClass) clazz.getDeclaredConstructor().newInstance(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } static class InnerClass { } }复制代码
Journal de sortie :
2020-03-19 22:34:49.738 2151-2151/? E/MainAc: totalTime: 15 2020-03-19 22:34:50.409 2151-2151/? E/MainAc: totalTime2: 670复制代码
utilise la réflexion pour générer 100 000 instances, ce qui prend 670 ms, ce qui est nettement supérieur aux 15 ms de l'utilisation directe du mot-clé new
, donc les performances de réflexion sont faibles . Ne vous inquiétez pas, cette conclusion est un peu précoce. Essayons de changer le nombre total d'instances à générer à 1 000. Le journal de sortie est :
2020-03-19 22:39:21.287 3641-3641/com.example.myapplication E/MainAc: totalTime: 2 2020-03-19 22:39:21.296 3641-3641/com.example.myapplication E/MainAc: totalTime2: 9复制代码
Utiliser la réflexion pour générer 1 000 instances, bien que cela prenne 9 ms, ce qui est supérieur à new
2 ms, mais la différence entre 9 ms et 2 ms est invisible à l'œil nu, et généralement la réflexion que nous écrivons en entreprise ne peut pas être exécutée plus de 1 000 fois. Dans ce scénario, nous pouvons à juste titre dire que le. les performances de réflexion sont très faibles.
Évidemment, non.
En plus du temps nécessaire à l’exécution du code, regardons l’impact de la réflexion sur la mémoire. Nous visons toujours à générer 100 000 instances et à apporter de légères modifications au code ci-dessus, en ne conservant tour à tour que la méthode new
et la méthode de réflexion, puis à exécuter le programme et à observer l'utilisation de la mémoire.
Utilisez la méthode new
Utilisez la réflexion
En comparant les deux images, nous pouvons voir qu'il y a beaucoup plus d'instances d'objet Constructor
et Class
dans la deuxième image, et ces deux parties occupent 2,7 Mo de mémoire. Par conséquent, nous pouvons conclure que la réflexion générera un grand nombre d’objets temporaires et occupera de l’espace mémoire supplémentaire.
Nous utilisons le code de l'instance de génération de réflexion dans l'expérience précédente comme point d'entrée.
Revoyez d'abord le cycle de vie des classes dans la machine virtuelle : chargement, connexion (vérification, préparation, analyse), initialisation, utilisation et désinstallation. Pendant le processus de chargement, la machine virtuelle convertit le bytecode de la classe en une structure de données d'exécution et l'enregistre dans la zone de méthode. Un objet java.lang.Class représentant la structure de données de cette classe sera généré dans la mémoire. à cette classe La structure des données est accessible via cet objet Class.
public InnerClass newInstanceByReflection() { // 获取虚拟机中 InnerClass 类的 Class 对象 Class clazz = InnerClass.class; try { return (InnerClass) clazz.getDeclaredConstructor().newInstance(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; }复制代码
Dans le code, clazz.getDeclaredConstructor()
est utilisé pour obtenir le constructeur défini dans la classe Puisque nous ne définissons pas explicitement le constructeur, nous renverrons le constructeur par défaut sans argument généré par le compilateur pour nous-mêmes.
Voyons comment getDeclaredConstructor
renvoie le constructeur. Les éléments suivants sont tous basés sur le code jdk 1.8 comme code source. La méthode
@CallerSensitivepublic Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { // 权限检查 checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); return getConstructor0(parameterTypes, Member.DECLARED); }复制代码
getDeclaredConstructor
effectue d'abord une vérification des autorisations, puis appelle directement la méthode getConstructor0
. La méthode
private Constructor<T> getConstructor0(Class<?>[] parameterTypes, int which) throws NoSuchMethodException{ // privateGetDeclaredConstructors 方法是获取所有的构造方法数组 Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); // 遍历所有的构造方法数组,根据传入的参数类型依次匹配,找到合适的构造方法后就会拷贝一份作为返回值 for (Constructor<T> constructor : constructors) { if (arrayContentsEq(parameterTypes, constructor.getParameterTypes())) { // 拷贝构造方法 return getReflectionFactory().copyConstructor(constructor); } } // 没有找到的话,就抛出异常 throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes)); }复制代码
getConstructor0
fait principalement deux choses :
Il n'y a pas grand chose à dire sur la correspondance traversale. Concentrons-nous sur la première chose, comment obtenir le tableau de toutes les méthodes de construction, qui est cette méthode privateGetDeclaredConstructors
.
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) { checkInitted(); Constructor<T>[] res; // 获取缓存的 ReflectionData 数据 ReflectionData<T> rd = reflectionData(); // 如果缓存中有 ReflectionData,就先看看 ReflectionData 中的 publicConstructors 或 declaredConstructors是否为空 if (rd != null) { res = publicOnly ? rd.publicConstructors : rd.declaredConstructors; if (res != null) return res; } // 如果没有缓存,或者缓存中构造方法数组为空 // No cached value available; request value from VM // 对接口类型的字节码特殊处理 if (isInterface()) { @SuppressWarnings("unchecked") // 如果是接口类型,那么生成一个长度为0的构造方法数组 Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0]; res = temporaryRes; } else { // 如果不是接口类型,就调用 getDeclaredConstructors0 获取构造方法数组 res = getDeclaredConstructors0(publicOnly); } // 获取到构造方法数组后,再赋值给缓存 ReflectionData 中的对应属性 if (rd != null) { if (publicOnly) { rd.publicConstructors = res; } else { rd.declaredConstructors = res; } } return res; }复制代码
Dans le code ci-dessus, j'ai commenté le code clé Avant d'expliquer tout le processus, nous avons vu un type inconnu ReflectionData
. Sa structure de données correspondante est :
private static class ReflectionData<T> { volatile Field[] declaredFields; volatile Field[] publicFields; volatile Method[] declaredMethods; volatile Method[] publicMethods; volatile Constructor<T>[] declaredConstructors; volatile Constructor<T>[] publicConstructors; // Intermediate results for getFields and getMethods volatile Field[] declaredPublicFields; volatile Method[] declaredPublicMethods; volatile Class<?>[] interfaces; // Value of classRedefinedCount when we created this ReflectionData instance final int redefinedCount; ReflectionData(int redefinedCount) { this.redefinedCount = redefinedCount; } }复制代码
ReflectionData
Cette classe est utilisée pour sauvegarder certaines données obtenues à partir de la machine virtuelle. En même temps, nous pouvons voir que tous les attributs de réflexion sont modifiés avec le mot-clé volatile
.
Obtenez les données ReflectionData
mises en cache en appelant la méthode reflectionData()
.
// 定义在 Class 类中的反射缓存对象private volatile transient SoftReference<ReflectionData<T>> reflectionData;private ReflectionData<T> reflectionData() { SoftReference<ReflectionData<T>> reflectionData = this.reflectionData; int classRedefinedCount = this.classRedefinedCount; ReflectionData<T> rd; if (useCaches && reflectionData != null && (rd = reflectionData.get()) != null && rd.redefinedCount == classRedefinedCount) { return rd; } // else no SoftReference or cleared SoftReference or stale ReflectionData // -> create and replace new instance return newReflectionData(reflectionData, classRedefinedCount); }复制代码
我们可以看到 reflectionData
实际上是一个软引用,软引用会在内存不足的情况下被虚拟机回收,所以reflectionData()
方法在开始的地方,先判断了是否可以使用缓存以及缓存是否失效,如果失效了,就会调用 newReflectionData
方法生成一个新的 ReflectionData
实例。
接下来看看 newReflectionData
方法。
private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData, int classRedefinedCount) { // 如果不允许使用缓存,直接返回 null if (!useCaches) return null; while (true) { ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount); // try to CAS it... if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) { return rd; } // else retry oldReflectionData = this.reflectionData; classRedefinedCount = this.classRedefinedCount; if (oldReflectionData != null && (rd = oldReflectionData.get()) != null && rd.redefinedCount == classRedefinedCount) { return rd; } } }复制代码
newReflectionData
中使用 volatile + 死循环 + CAS 机制 保证线程安全。注意到这里的死循环每执行一次都会构造一个新的 ReflectionData
实例。
你可能会有疑问,Class
中 reflectionData
属性什么时候被赋值的,其实是封装在Atomic.casReflectionData
这个方法里了,他会检测当前Class
对象中的reflectionData
是否与oldReflectionData
相等,如果相等,就会把new SoftReference<>(rd)
赋值给 reflectionData
。
到现在为止,关于 ReflectionData
的背景知识都介绍完了。我们再回到 privateGetDeclaredConstructors
中看看获取构造方法的流程。
privateGetDeclaredConstructors
流程图
可以看到对于普通类,最终通过调用 getDeclaredConstructors0
方法获取的构造方法列表。
private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);复制代码
这个方法是 native 的,具体逻辑在 jdk 源码中。
在 native/java/lang/Class_getDeclaredConstructors0.c
文件中,
void getDeclaredConstructors0(Frame * frame){ // Frame 可以理解为调用native方法时,java层传递过来的数据的一种封装 LocalVars * vars = frame->localVars; Object * classObj = getLocalVarsThis(vars); // 取得java方法的入参 bool publicOnly = getLocalVarsBoolean(vars, 1); uint16_t constructorsCount = 0; // 获取要查询的类的 Class 对象 Class * c = classObj->extra; // 获取这个类的所有构造方法,且数量保存在 constructorsCount 中 Method* * constructors = getClassConstructors(c, publicOnly, &constructorsCount); // 获取 java 方法调用所属的 classLoader ClassLoader * classLoader = frame->method->classMember.attachClass->classLoader; // 拿到 Constructor 对应的 class 对象 Class * constructorClass = loadClass(classLoader, "java/lang/reflect/Constructor"); //创建一个长度为 constructorsCount 的数组保存构造方法 Object * constructorArr = newArray(arrayClass(constructorClass), constructorsCount); pushOperandRef(frame->operandStack, constructorArr); // 后面是具体的赋值逻辑。将native中的Method对象转化为java层的Constructor对象 if (constructorsCount > 0) { Thread * thread = frame->thread; Object* * constructorObjs = getObjectRefs(constructorArr); Method * constructorInitMethod = getClassConstructor(constructorClass, _constructorConstructorDescriptor); for (uint16_t i = 0; i < constructorsCount; i++) { Method * constructor = constructors[i]; Object * constructorObj = newObject(constructorClass); constructorObj->extra = constructor; constructorObjs[i] = constructorObj; OperandStack * ops = newOperandStack(9); pushOperandRef(ops, constructorObj); pushOperandRef(ops, classObj); pushOperandRef(ops, toClassArr(classLoader, methodParameterTypes(constructor), constructor->parsedDescriptor->parameterTypesCount)); if (constructor->exceptions != NULL) pushOperandRef(ops, toClassArr(classLoader, methodExceptionTypes(constructor), constructor->exceptions->number_of_exceptions)); else pushOperandRef(ops, toClassArr(classLoader, methodExceptionTypes(constructor), 0)); pushOperandInt(ops, constructor->classMember.accessFlags); pushOperandInt(ops, 0); pushOperandRef(ops, getSignatureStr(classLoader, constructor->classMember.signature)); // signature pushOperandRef(ops, toByteArr(classLoader, constructor->classMember.annotationData, constructor->classMember.annotationDataLen)); pushOperandRef(ops, toByteArr(classLoader, constructor->parameterAnnotationData, constructor->parameterAnnotationDataLen)); Frame * shimFrame = newShimFrame(thread, ops); pushThreadFrame(thread, shimFrame); // init constructorObj InvokeMethod(shimFrame, constructorInitMethod); } } }复制代码
从上面的逻辑,可以知道获取构造方法的核心方法是 getClassConstructors
,所在文件为 rtda/heap/class.c
。
Method* * getClassConstructors(Class * self, bool publicOnly, uint16_t * constructorsCount){ // 分配大小为 sizeof(Method) 的长度为 methodsCount 的连续内存地址,即数组 Method* * constructors = calloc(self->methodsCount, sizeof(Method)); *constructorsCount = 0; // 在native 层,构造方法和普通方法都存在 methods 中,逐一遍历 for (uint16_t i = 0; i < self->methodsCount; i++) { Method * method = self->methods + i; // 判断是否是构造方法 if (isMethodConstructor(method)) { // 检查权限 if (!publicOnly || isMethodPublic(method)) { // 符合条件的构造方法依次存到数组中 constructors[*constructorsCount] = method; (*constructorsCount)++; } } } return constructors; }复制代码
可以看到getClassConstructors
实际上就是对 methods
进行了一次过滤,过滤的条件为:1.是构造方法;2.权限一致。
isMethodConstructor
方法的判断逻辑也是十分简单,不是静态方法,而且方法名是<init>
即可。
bool isMethodConstructor(Method * self){ return !isMethodStatic(self) && strcmp(self->classMember.name, "<init>") == 0; }复制代码
所以核心的逻辑变成了Class
中的 methods
数组何时被初始化赋值的?我们刨根问底的追踪下。
我们先找到类加载到虚拟机中的入口方法 loadNonArrayClass
:
Class * loadNonArrayClass(ClassLoader * classLoader, const char * className){ int32_t classSize = 0; char * classContent = NULL; Class * loadClass = NULL; classSize = readClass(className, &classContent); if (classSize > 0 && classContent != NULL){#if 0 printf("class size:%d,class data:[", classSize); for (int32_t i = 0; i < classSize; i++) { printf("0x%02x ", classContent[i]); } printf("]\n");#endif } if (classSize <= 0) { printf("Could not found target class\n"); exit(127); } // 解析字节码文件 loadClass = parseClassFile(classContent, classSize); loadClass->classLoader = classLoader; // 加载 defineClass(classLoader, loadClass); // 链接 linkClass(classLoader, loadClass); //printf("[Loaded %s\n", loadClass->name); return loadClass; }复制代码
在 parseClassFile
方法中,调用了newClass
方法。
Class * parseClassFile(char * classContent, int32_t classSize){ ClassFile * classFile = NULL; classFile = parseClassData(classContent, classSize); return newClass(classFile); }复制代码
newClass
方法在rtda/heap/class.c
文件中。
Class * newClass(ClassFile * classFile){ Class * c = calloc(1, sizeof(Class)); c->accessFlags = classFile->accessFlags; c->sourceFile = getClassSourceFileName(classFile); newClassName(c, classFile); newSuperClassName(c, classFile); newInterfacesName(c, classFile); newConstantPool(c, classFile); newFields(c, classFile); newMethods(c, classFile); return c; }复制代码
可以看到,在native层创建了一个Class
对象,我们重点看newMethods(c, classFile)
方法啊,这个方法定义在rtda/heap/method.c
中。
Method * newMethods(struct Class * c, ClassFile * classFile){ c->methodsCount = classFile->methodsCount; c->methods = NULL; if (c->methodsCount == 0) return NULL; c->methods = calloc(classFile->methodsCount, sizeof(Method)); for (uint16_t i = 0; i < c->methodsCount; i++) { c->methods[i].classMember.attachClass = c; copyMethodInfo(&c->methods[i], &classFile->methods[i], classFile); copyAttributes(&c->methods[i], &classFile->methods[i], classFile); MethodDescriptor * md = parseMethodDescriptor(c->methods[i].classMember.descriptor); c->methods[i].parsedDescriptor = md; calcArgSlotCount(&c->methods[i]); if (isMethodNative(&c->methods[i])) { injectCodeAttribute(&c->methods[i], md->returnType); } } return NULL; }复制代码
上述代码可以看出,实际上就是把ClassFile
中解析到的方法逐一赋值给了 Class
对象的 methods
数组。
总算梳理清楚了,反射创建对象的调用链为:
loadClass -> loadNonArrayClass -> parseClassFile -> newMethods -> Class 的 methods数组 privateGetDeclaredConstructors -> getDeclaredConstructors0 -> getClassConstructors (过滤Class 的 methods数组)复制代码
到目前为止,我们搞明白反射时如何找到对应的构造方法的。下面我们来看 newInstance
方法。
(InnerClass) clazz.getDeclaredConstructor().newInstance();复制代码
public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 构造方法是否被重载了 if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); // 检查权限 checkAccess(caller, clazz, null, modifiers); } } // 枚举类型报错 if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); // ConstructorAccessor 是缓存的,如果为空,就去创建一个 ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { // 创建 ConstructorAccessor ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") // 使用 ConstructorAccessor 的 newInstance 构造实例 T inst = (T) ca.newInstance(initargs); return inst; }复制代码
接着看下 acquireConstructorAccessor
方法。
private ConstructorAccessor acquireConstructorAccessor() { // First check to see if one has been created yet, and take it // if so. ConstructorAccessor tmp = null; // 可以理解为缓存的对象 if (root != null) tmp = root.getConstructorAccessor(); if (tmp != null) { constructorAccessor = tmp; } else { // Otherwise fabricate one and propagate it up to the root // 生成一个 ConstructorAccessor,并缓存起来 tmp = reflectionFactory.newConstructorAccessor(this); setConstructorAccessor(tmp); } return tmp; }复制代码
继续走到newConstructorAccessor
方法。
public ConstructorAccessor newConstructorAccessor(Constructor<?> var1) { checkInitted(); Class var2 = var1.getDeclaringClass(); // 如果是抽象类,报错 if (Modifier.isAbstract(var2.getModifiers())) { return new InstantiationExceptionConstructorAccessorImpl((String)null); } // 如果 Class 类报错 else if (var2 == Class.class) { return new InstantiationExceptionConstructorAccessorImpl("Can not instantiate java.lang.Class"); } // 如果是 ConstructorAccessorImpl 的子类的话,返回 BootstrapConstructorAccessorImpl else if (Reflection.isSubclassOf(var2, ConstructorAccessorImpl.class)) { return new BootstrapConstructorAccessorImpl(var1); } // 判断 noInflation , 后面是判断不是匿名类 else if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) { return (new MethodAccessorGenerator()).generateConstructor(var1.getDeclaringClass(), var1.getParameterTypes(), var1.getExceptionTypes(), var1.getModifiers()); } // 使用 NativeConstructorAccessorImpl 来生成实例 else { NativeConstructorAccessorImpl var3 = new NativeConstructorAccessorImpl(var1); DelegatingConstructorAccessorImpl var4 = new DelegatingConstructorAccessorImpl(var3); var3.setParent(var4); return var4; } }复制代码
具体逻辑,在上述代码中已经注释了。这里提一下 noInflation
。
ReflectionFactory
在执行所有方法前会检查下是否执行过了checkInitted
方法,这个方法会把noInflation
的值和inflationThreshold
从虚拟机的环境变量中读取出来并赋值。
当noInflation
为 false
而且不是匿名类时,就会使用MethodAccessorGenerator
方式。否则就是用 NativeConstructorAccessorImpl
的方式来生成。
默认noInflation
为false
,所以我们先看native调用的方式。关注 NativeConstructorAccessorImpl
类。
class NativeConstructorAccessorImpl extends ConstructorAccessorImpl { private final Constructor<?> c; private DelegatingConstructorAccessorImpl parent; private int numInvocations; NativeConstructorAccessorImpl(Constructor<?> var1) { this.c = var1; } public Object newInstance(Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException { if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.c.getDeclaringClass())) { ConstructorAccessorImpl var2 = (ConstructorAccessorImpl)(new MethodAccessorGenerator()).generateConstructor(this.c.getDeclaringClass(), this.c.getParameterTypes(), this.c.getExceptionTypes(), this.c.getModifiers()); this.parent.setDelegate(var2); } return newInstance0(this.c, var1); } void setParent(DelegatingConstructorAccessorImpl var1) { this.parent = var1; } private static native Object newInstance0(Constructor<?> var0, Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException; }复制代码
我们可以看到 NativeConstructorAccessorImpl
中维护了一个计数器numInvocations
,在每次调用newInstance
方法生成实例时,就会对计数器自增,当计数器超过ReflectionFactory.inflationThreshold()
的阈值,默认为15,就会使用 ConstructorAccessorImpl
替换 NativeConstructorAccessorImpl
,后面就会直接调用MethodAccessorGenerator
中的方法了。
我们先看看没到达阈值前,会调用native方法 newInstance0
,这个方法定义在native/sun/reflect/NativeConstructorAccessorImpl.c
中,具体newInstance0
的流程我就不分析了,大致逻辑是操作堆栈执行方法。
然后我们再看看超过阈值后,执行的是 MethodAccessorGenerator
生成构造器的方式。这种方式与newConstructorAccessor
方法中noInflation
为 false
的处理方式一样。所以可以解释为:java虚拟机在执行反射操作时,如果同一操作执行次数超过阈值,会从native生成实例的方式转变为java生成实例的方式。
MethodAccessorGenerator
的MethodAccessorGenerator
方法如下。
public ConstructorAccessor generateConstructor(Class<?> var1, Class<?>[] var2, Class<?>[] var3, int var4) { return (ConstructorAccessor)this.generate(var1, "<init>", var2, Void.TYPE, var3, var4, true, false, (Class)null); }复制代码
继续跟踪下去可以发现,反射调用构造方法实际上是动态编写字节码,并且在虚拟机中把编好的字节码加载成一个Class,这个Class实际上是 ConstructorAccessorImpl
类型的,然后调用这个动态类的newInstance
方法。回看刚刚我们梳理的newConstructorAccessor
代码,可以看到第三个逻辑:
// 如果是 ConstructorAccessorImpl 的子类的话,返回 BootstrapConstructorAccessorImpl else if (Reflection.isSubclassOf(var2, ConstructorAccessorImpl.class)) { return new BootstrapConstructorAccessorImpl(var1); } 复制代码
最终执行的是 BootstrapConstructorAccessorImpl
的newInstance
方法。
class BootstrapConstructorAccessorImpl extends ConstructorAccessorImpl { private final Constructor<?> constructor; BootstrapConstructorAccessorImpl(Constructor<?> var1) { this.constructor = var1; } public Object newInstance(Object[] var1) throws IllegalArgumentException, InvocationTargetException { try { return UnsafeFieldAccessorImpl.unsafe.allocateInstance(this.constructor.getDeclaringClass()); } catch (InstantiationException var3) { throw new InvocationTargetException(var3); } } }复制代码
最后是通过使用Unsafe
类分配了一个实例。
到现在为止,我们已经把反射生成实例的所有流程都搞清楚了。回到文章开头的问题,我们现在反思下,反射性能低么?为什么?
在Android中,我们可以在某些情况下对反射进行优化。举个例子,EventBus 2.x 会在 register 方法运行时,遍历所有方法找到回调方法;而EventBus 3.x 则在编译期间,将所有回调方法的信息保存的自己定义的 SubscriberMethodInfo
中,这样可以减少对运行时的性能影响。
想了解更多相关学习,敬请关注php培训栏目!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!