日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

大家都说 Java 反射效率低,你知道原因在哪里么

發布時間:2023/12/15 java 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大家都说 Java 反射效率低,你知道原因在哪里么 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這是 ZY 第 17 篇原創技術文章

預備知識

  • 了解 Java 反射基本用法
  • 看完本文可以達到什么程度

  • 了解 Java 反射原理及 Java 反射效率低的原因
  • 文章概覽

    我們在 Java 開發中,難免會接觸到反射,而在一些框架中,反射的運用更是常見。我相信,每次提到反射,大家的第一反應一定是反射效率低,盡量少使用。
    但是反射的效率到底低多少?反射效率低的原因在哪里?
    這篇文章就來探索一下這些問題。
    由于本機上安裝的是 openjdk 12,所以這里就使用 openjdk 12 源碼進行分析。

    我們先看結論,然后分析一下 Java 反射的原理,過程中大家可以根據結論,對源碼做一些思考,然后再根據原理中的一些實現,看看 Java 反射效率低的原因。

    零、先放結論

    Java 反射效率低主要原因是:

  • Method#invoke 方法會對參數做封裝和解封操作
  • 需要檢查方法可見性
  • 需要校驗參數
  • 反射方法難以內聯
  • JIT 無法優化
  • 一、Java 反射原理--獲取要反射的方法

    1.1 反射的使用

    我們先來看看 Java 反射使用的一段代碼:

    public class RefTest {public static void main(String[] args) {try {Class clazz = Class.forName("com.zy.java.RefTest");Object refTest = clazz.newInstance();Method method = clazz.getDeclaredMethod("refMethod");method.invoke(refTest);} catch (Exception e) {e.printStackTrace();}}public void refMethod() {} } 復制代碼

    我們在調用反射時,首先會創建 Class 對象,然后獲取其 Method 對象,調用 invoke 方法。
    獲取反射方法時,有兩個方法,getMethod 和 getDeclaredMethod,我們就從這兩個方法開始,一步步看下反射的原理。
    接下來就進入代碼分析,大家做好準備。

    1.2 getMethod / getDeclaredMethod

    這里我們先整體看一下 getMethod 和 getDeclaredMethod 的實現。

    class Class {@CallerSensitivepublic Method getMethod(String name, Class<?>... parameterTypes)throws NoSuchMethodException, SecurityException {Objects.requireNonNull(name);SecurityManager sm = System.getSecurityManager();if (sm != null) {// 1. 檢查方法權限checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);}// 2. 獲取方法Method method = getMethod0(name, parameterTypes);if (method == null) {throw new NoSuchMethodException(methodToString(name, parameterTypes));}// 3. 返回方法的拷貝return getReflectionFactory().copyMethod(method);}@CallerSensitivepublic Method getDeclaredMethod(String name, Class<?>... parameterTypes)throws NoSuchMethodException, SecurityException {Objects.requireNonNull(name);SecurityManager sm = System.getSecurityManager();if (sm != null) {// 1. 檢查方法是權限checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);}// 2. 獲取方法Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);if (method == null) {throw new NoSuchMethodException(methodToString(name, parameterTypes));}// 3. 返回方法的拷貝return getReflectionFactory().copyMethod(method);} } 復制代碼

    從上面的代碼,我們可以看到,獲取方法的流程分三步走:

  • 檢查方法權限
  • 獲取方法 Method 對象
  • 返回方法的拷貝
  • 這里主要有兩個區別:

  • getMethod 中 checkMemberAccess 傳入的是 Member.PUBLIC,而 getDeclaredMethod 傳入的是 Member.DECLARED 這兩個值有什么區別呢?我們看下代碼中的注釋:
  • interface Member {/*** Identifies the set of all public members of a class or interface,* including inherited members.*/public static final int PUBLIC = 0;/*** Identifies the set of declared members of a class or interface.* Inherited members are not included.*/public static final int DECLARED = 1; } 復制代碼

    注釋里清楚的解釋了 PUBLIC 和 DECLARED 的不同,PUBLIC 會包括所有的 public 方法,包括父類的方法,而 DECLARED 會包括所有自己定義的方法,public,protected,private 都在此,但是不包括父類的方法。
    這也正是 getMethod 和 getDeclaredMethod 的區別。
    2. getMethod 中獲取方法調用的是 getMethod0,而 getDeclaredMethod 獲取方法調用的是 privateGetDeclaredMethods 關于這個區別,這里簡單提及一下,后面具體分析代碼。
    privateGetDeclaredMethods 是獲取類自身定義的方法,參數是 boolean publicOnly,表示是否只獲取公共方法。

    private Method[] privateGetDeclaredMethods(boolean publicOnly) {//... } 復制代碼

    而 getMethod0 會遞歸查找父類的方法,其中會調用到 privateGetDeclaredMethods 方法。

    既然我們上面看了 getMethod 和 getDeclaredMethod 的區別,我們自然選擇 getMethod 方法進行分析,這樣可以走到整個流程。

    1.3 getMethod 方法

    getMethod 方法流程如下圖:

    class Class {public Method getMethod(String name, Class<?>... parameterTypes)throws NoSuchMethodException, SecurityException {Objects.requireNonNull(name);SecurityManager sm = System.getSecurityManager();if (sm != null) {// 1. 檢查方法權限checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);}// 2. 獲取方法 Method 對象Method method = getMethod0(name, parameterTypes);if (method == null) {throw new NoSuchMethodException(methodToString(name, parameterTypes));}// 3. 返回方法拷貝return getReflectionFactory().copyMethod(method);} } 復制代碼

    我們上面說到獲取方法分三步走:

  • 檢查方法權限
  • 獲取方法 Method 對象
  • 返回方法的拷貝
  • 我們先看看檢查方法權限做了些什么事情。

    1.3.1 checkMemberAccess
    class Class {private void checkMemberAccess(SecurityManager sm, int which,Class<?> caller, boolean checkProxyInterfaces) {/* Default policy allows access to all {@link Member#PUBLIC} members,* as well as access to classes that have the same class loader as the caller.* In all other cases, it requires RuntimePermission("accessDeclaredMembers")* permission.*/final ClassLoader ccl = ClassLoader.getClassLoader(caller);if (which != Member.PUBLIC) {final ClassLoader cl = getClassLoader0();if (ccl != cl) {sm.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);}}this.checkPackageAccess(sm, ccl, checkProxyInterfaces);} } 復制代碼

    在這里可以看到,對于非 Member.PUBLIC 的訪問,會增加一項檢測,SecurityManager.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); 這項檢測需要運行時申請 RuntimePermission("accessDeclaredMembers")。
    這里就不繼續往下看了,方法整體是在檢查是否可以訪問對象成員。

    接著看下是如何獲取方法的 Method 對象。

    1.3.2 getMethod0
    class Class {private Method getMethod0(String name, Class<?>[] parameterTypes) {PublicMethods.MethodList res = getMethodsRecursive(name,parameterTypes == null ? EMPTY_CLASS_ARRAY : parameterTypes,/* includeStatic */ true);return res == null ? null : res.getMostSpecific();} } 復制代碼

    這里是通過 getMethodsRecursive 獲取到 MethodList 對象,然后通過 MethodList#getMostSpecific 方法篩選出對應的方法。 MethodList#getMOstSpecific 會篩選返回值類型最為具體的方法,至于為什么會有返回值的區別,后面會講到。
    (這里的具體,指的是有兩個方法,返回值分別是 Child 和 Parent,Child 繼承自 Parent,這里會篩選出返回值為 Child 的方法)。

    接著看 getMethodsRecursive 方法,是如何獲取方法的。

    1.3.3 getMethodsRecursive
    class Class {private PublicMethods.MethodList getMethodsRecursive(String name,Class<?>[] parameterTypes,boolean includeStatic) {// 1. 獲取自己的 public 方法Method[] methods = privateGetDeclaredMethods(/* publicOnly */ true);// 2. 篩選符合條件的方法,構造 MethodList 對象PublicMethods.MethodList res = PublicMethods.MethodList.filter(methods, name, parameterTypes, includeStatic);// 找到方法,直接返回if (res != null) {return res;}// 3. 沒有找到方法,就獲取其父類,遞歸調用 getMethodsRecursive 方法Class<?> sc = getSuperclass();if (sc != null) {res = sc.getMethodsRecursive(name, parameterTypes, includeStatic);}// 4. 獲取接口中對應的方法for (Class<?> intf : getInterfaces(/* cloneArray */ false)) {res = PublicMethods.MethodList.merge(res, intf.getMethodsRecursive(name, parameterTypes,/* includeStatic */ false));}return res;} } 復制代碼

    這里獲取方法有四個步驟:

  • 通過 privateGetDeclaredMethods 獲取自己所有的 public 方法
  • 通過 MethodList#filter 查找 方法名,參數相同的方法,如果找到,直接返回
  • 如果自己沒有實現對應的方法,就去父類中查找對應的方法
  • 查找接口中對應的方法
  • 通過上面四個步驟,最終獲取到的是一個 MethodList 對象,是一個鏈表結點,其 next 指向下一個結點。也就是說,這里獲取到的 Method 會有多個。
    這里稍微解釋一下,在我們平時編寫 Java 代碼時,同一個類是不能有方法名和方法參數都相同的方法的,而實際上,在 JVM 中,一個方法簽名是和 返回值,方法名,方法參數 三者相關的。 也就是說,在 JVM 中,可以存在 方法名和方法參數都相同,但是返回值不同的方法。
    所以這里返回的是一個方法鏈表。
    所以上面最終返回方法時會通過 MethodList#getMostSpecific 進行返回值的篩選,篩選出返回值類型最具體的方法。

    這里我們先暫?;仡櫼幌抡w的調用鏈路:

    getMethod -> getMethod0 -> getMethodsRecursive -> privateGetDeclaredMethods 復制代碼

    通過函數調用,最終會調用到 privateGetDeclaredMethods 方法,也就是真正獲取方法的地方。

    1.3.4 privateGetDeclaredMethods
    class Class {private Method[] privateGetDeclaredMethods(boolean publicOnly) {Method[] res;// 1. 通過緩存獲取 Method[]ReflectionData<T> rd = reflectionData();if (rd != null) {res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;if (res != null) return res;}// 2. 沒有緩存,通過 JVM 獲取res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));if (rd != null) {if (publicOnly) {rd.declaredPublicMethods = res;} else {rd.declaredMethods = res;}}return res;} } 復制代碼

    在 privateGetDeclaredMethods 獲取方法時,有兩個步驟:

  • relectionData 通過緩存獲取
  • 如果緩存沒有命中的話,通過 getDeclaredMethods0 獲取方法
  • 先看看 relectionData 方法:

    class Class {private ReflectionData<T> reflectionData() {SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;int classRedefinedCount = this.classRedefinedCount;ReflectionData<T> rd;if (reflectionData != null &&(rd = reflectionData.get()) != null &&rd.redefinedCount == classRedefinedCount) {return rd;}// else no SoftReference or cleared SoftReference or stale ReflectionData// -> create and replace new instancereturn newReflectionData(reflectionData, classRedefinedCount);} } 復制代碼

    在 Class 中會維護一個 ReflectionData 的軟引用,作為反射數據的緩存。
    ReflectionData 結構如下:

    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 getMethodsvolatile Field[] declaredPublicFields;volatile Method[] declaredPublicMethods;volatile Class<?>[] interfaces;// Cached namesString simpleName;String canonicalName;static final String NULL_SENTINEL = new String();// Value of classRedefinedCount when we created this ReflectionData instancefinal int redefinedCount;} 復制代碼

    可以看到,保存了 Class 中的屬性和方法。 如果緩存為空,就會通過 getDeclaredMethods0 從 JVM 中查找方法。
    getDeclaredMethods0 是一個 native 方法,這里暫時先不看。

    通過上面幾個步驟,就獲取到 Method 數組。

    這就是 getMethod 方法的整個實現了。
    我們再回過頭看一下 getDeclaredMethod 方法的實現,通過 privateGetDeclaredMethods 獲取方法以后,會通過 searchMethods 對方法進行篩選。

    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)throws NoSuchMethodException, SecurityException {// ...Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);// ...} 復制代碼

    searchMethods 方法實現比較簡單,就是對比方法名,參數,方法返回值。

    class Class {private static Method searchMethods(Method[] methods,String name,Class<?>[] parameterTypes){ReflectionFactory fact = getReflectionFactory();Method res = null;for (Method m : methods) {// 比較方法名if (m.getName().equals(name)// 比較方法參數&& arrayContentsEq(parameterTypes,fact.getExecutableSharedParameterTypes(m))// 比較返回值&& (res == null|| (res.getReturnType() != m.getReturnType()&& res.getReturnType().isAssignableFrom(m.getReturnType()))))res = m;}return res;} } 復制代碼
    1.3.5 Method#copy

    在獲取到對應方法以后,并不會直接返回,而是會通過 getReflectionFactory().copyMethod(method); 返回方法的一個拷貝。
    最終調用的是 Method#copy,我們來看看其實現。

    class Method {Method copy() {// This routine enables sharing of MethodAccessor objects// among Method objects which refer to the same underlying// method in the VM. (All of this contortion is only necessary// because of the "accessibility" bit in AccessibleObject,// which implicitly requires that new java.lang.reflect// objects be fabricated for each reflective call on Class// objects.)if (this.root != null)throw new IllegalArgumentException("Can not copy a non-root Method");Method res = new Method(clazz, name, parameterTypes, returnType,exceptionTypes, modifiers, slot, signature,annotations, parameterAnnotations, annotationDefault);res.root = this;// Might as well eagerly propagate this if already presentres.methodAccessor = methodAccessor;return res;} } 復制代碼

    會 new 一個 Method 實例并返回。
    這里有兩點要注意:

  • 設置 root = this
  • 會給 Method 設置 MethodAccessor,用于后面方法調用。也就是所有的 Method 的拷貝都會使用同一份 methodAccessor。
  • 通過上面的步驟,就獲取到了需要反射的方法。
    我們再回顧一下之前的流程。

    二、Java 反射原理--調用反射方法

    獲取到方法以后,通過 Method#invoke 調用方法。

    class Method {public Object invoke(Object obj, Object... args)throws IllegalAccessException, IllegalArgumentException,InvocationTargetException{if (!override) {Class<?> caller = Reflection.getCallerClass();// 1. 檢查權限checkAccess(caller, clazz,Modifier.isStatic(modifiers) ? null : obj.getClass(),modifiers);}// 2. 獲取 MethodAccessorMethodAccessor ma = methodAccessor; // read volatileif (ma == null) {// 創建 MethodAccessorma = acquireMethodAccessor();}// 3. 調用 MethodAccessor.invokereturn ma.invoke(obj, args);} } 復制代碼

    invoke 方法的實現,分為三步:

    2.1 檢查是否有權限調用方法

    這里對 override 變量進行判斷,如果 override == true,就跳過檢查 我們通常在 Method#invoke 之前,會調用 Method#setAccessible(true),就是設置 override 值為 true。

    2.2 獲取 MethodAccessor

    在上面獲取 Method 的時候我們講到過,Method#copy 會給 Method 的 methodAccessor 賦值。所以這里的 methodAccessor 就是拷貝時使用的 MethodAccessor。
    如果 ma 為空,就去創建 MethodAccessor。

    class Method {private MethodAccessor acquireMethodAccessor() {// First check to see if one has been created yet, and take it// if soMethodAccessor tmp = null;if (root != null) tmp = root.getMethodAccessor();if (tmp != null) {methodAccessor = tmp;} else {// Otherwise fabricate one and propagate it up to the roottmp = reflectionFactory.newMethodAccessor(this);setMethodAccessor(tmp);}return tmp;} } 復制代碼

    這里會先查找 root 的 MethodAccessor,這里的 root 在上面 Method#copy 中設置過。
    如果還是沒有找到,就去創建 MethodAccessor。

    class ReflectionFactory {public MethodAccessor newMethodAccessor(Method method) {// 其中會對 noInflation 進行賦值checkInitted();// ...if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {// 生成的是 MethodAccessorImplreturn 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;}} } 復制代碼

    這里可以看到,一共有三種 MethodAccessor。MethodAccessorImpl,NativeMethodAccessorImpl,DelegatingMethodAccessorImpl。
    采用哪種 MethodAccessor 根據 noInflation 進行判斷,noInflation 默認值為 false,只有指定了 sun.reflect.noInflation 屬性為 true,才會 采用 MethodAccessorImpl。
    所以默認會調用 NativeMethodAccessorImpl。

    MethodAccessorImpl 是通過動態生成字節碼來進行方法調用的,是 Java 版本的 MethodAccessor,字節碼生成比較復雜,這里不放代碼了。大家感興趣可以看這里的 generate 方法。

    DelegatingMethodAccessorImpl 就是單純的代理,真正的實現還是 NativeMethodAccessorImpl。

    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;} } 復制代碼

    NativeMethodAccessorImpl 是 Native 版本的 MethodAccessor 實現。

    class NativeMethodAccessorImpl extends MethodAccessorImpl {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())) {// Java 版本的 MethodAccessorMethodAccessorImpl acc = (MethodAccessorImpl)new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),method.getName(),method.getParameterTypes(),method.getReturnType(),method.getExceptionTypes(),method.getModifiers());parent.setDelegate(acc);}// Native 版本調用return invoke0(method, obj, args);}private static native Object invoke0(Method m, Object obj, Object[] args); } 復制代碼

    在 NativeMethodAccessorImpl 的實現中,我們可以看到,有一個 numInvocations 閥值控制,numInvocations 表示調用次數。如果 numInvocations 大于 15(默認閥值是 15),那么就使用 Java 版本的 MethodAccessorImpl。

    為什么采用這個策略呢,可以 JDK 中的注釋:

    // "Inflation" mechanism. Loading bytecodes to implement// Method.invoke() and Constructor.newInstance() currently costs// 3-4x more than an invocation via native code for the first// invocation (though subsequent invocations have been benchmarked// to be over 20x faster). Unfortunately this cost increases// startup time for certain applications that use reflection// intensively (but only once per class) to bootstrap themselves.// To avoid this penalty we reuse the existing JVM entry points// for the first few invocations of Methods and Constructors and// then switch to the bytecode-based implementations.//// Package-private to be accessible to NativeMethodAccessorImpl// and NativeConstructorAccessorImplprivate static boolean noInflation = false; 復制代碼

    Java 版本的 MethodAccessorImpl 調用效率比 Native 版本要快 20 倍以上,但是 Java 版本加載時要比 Native 多消耗 3-4 倍資源,所以默認會調用 Native 版本,如果調用次數超過 15 次以后,就會選擇運行效率更高的 Java 版本。
    那為什么 Native 版本運行效率會沒有 Java 版本高呢?從 R 大博客來看,是因為 這是HotSpot的優化方式帶來的性能特性,同時也是許多虛擬機的共同點:跨越native邊界會對優化有阻礙作用,它就像個黑箱一樣讓虛擬機難以分析也將其內聯,于是運行時間長了之后反而是托管版本的代碼更快些。

    2.3 調用 MethodAccessor#invoke 實現方法的調用

    在生成 MethodAccessor 以后,就調用其 invoke 方法進行最終的反射調用。
    這里我們對 Java 版本的 MethodAccessorImpl 做個簡單的分析,Native 版本暫時不做分析。
    在前面我們提到過 MethodAccessorImpl 是通過 MethodAccessorGenerator#generate 生成動態字節碼然后動態加載到 JVM 中的。
    其中生成 invoke 方法字節碼的是 MethodAccessorGenerator#emitInvoke。
    我們看其中校驗參數的一小段代碼:

    // Iterate through incoming actual parameters, ensuring that each// is compatible with the formal parameter type, and pushing the// actual on the operand stack (unboxing and widening if necessary).// num args of other invoke bytecodesfor (int i = 0; i < parameterTypes.length; i++) {// ...if (isPrimitive(paramType)) {// Unboxing code.// Put parameter into temporary local variable// astore_3 | astore_2// ...// repeat for all possible widening conversions:// aload_3 | aload_2// instanceof <primitive boxing type>// ifeq <next unboxing label>// aload_3 | aload_2// checkcast <primitive boxing type> // Note: this is "redundant",// // but necessary for the verifier// invokevirtual <unboxing method>// <widening conversion bytecode, if necessary>// goto <next parameter label>// <next unboxing label:> ...// last unboxing label:// new <IllegalArgumentException>// dup// invokespecial <IllegalArgumentException ctor>// athrow}} 復制代碼

    通過上面的注釋以及字節碼,我們可以看到,生成的 invoke 方法,會對傳入的參數做校驗,其中會涉及到 unboxing 操作。

    到此,基本上 Java 方法反射的原理就介紹完了。

    三、Java 反射效率低的原因

    了解了反射的原理以后,我們來分析一下反射效率低的原因。

    1. Method#invoke 方法會對參數做封裝和解封操作

    我們可以看到,invoke 方法的參數是 Object[] 類型,也就是說,如果方法參數是簡單類型的話,需要在此轉化成 Object 類型,例如 long ,在 javac compile 的時候 用了Long.valueOf() 轉型,也就大量了生成了Long 的 Object, 同時 傳入的參數是Object[]數值,那還需要額外封裝object數組。
    而在上面 MethodAccessorGenerator#emitInvoke 方法里我們看到,生成的字節碼時,會把參數數組拆解開來,把參數恢復到沒有被 Object[] 包裝前的樣子,同時還要對參數做校驗,這里就涉及到了解封操作。
    因此,在反射調用的時候,因為封裝和解封,產生了額外的不必要的內存浪費,當調用次數達到一定量的時候,還會導致 GC。

    2. 需要檢查方法可見性

    通過上面的源碼分析,我們會發現,反射時每次調用都必須檢查方法的可見性(在 Method.invoke 里)

    3. 需要校驗參數

    反射時也必須檢查每個實際參數與形式參數的類型匹配性(在NativeMethodAccessorImpl.invoke0 里或者生成的 Java 版 MethodAccessor.invoke 里);

    4. 反射方法難以內聯

    Method#invoke 就像是個獨木橋一樣,各處的反射調用都要擠過去,在調用點上收集到的類型信息就會很亂,影響內聯程序的判斷,使得 Method.invoke() 自身難以被內聯到調用方。參見 www.iteye.com/blog/rednax…

    5. JIT 無法優化

    在 JavaDoc 中提到:

    Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.

    因為反射涉及到動態加載的類型,所以無法進行優化。

    總結

    上面就是對反射原理和反射效率低的一些分析。

    參考資料

    www.iteye.com/blog/rednax…

    About Me

    總結

    以上是生活随笔為你收集整理的大家都说 Java 反射效率低,你知道原因在哪里么的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。