大家都說 Java 反射效率低,你知道原因在哪裡么?

  • 2019 年 11 月 25 日
  • 筆記

預備知識

  1. 了解 Java 反射基本用法

看完本文可以達到什麼程度

  1. 了解 Java 反射原理及 Java 反射效率低的原因

文章概覽

summary

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

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

零、先放結論

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

  1. Method#invoke 方法會對參數做封裝和解封操作
  2. 需要檢查方法可見性
  3. 需要校驗參數
  4. 反射方法難以內聯
  5. JIT 無法優化

1. 原理–獲取要反射的方法

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 方法。獲取反射方法時,有兩個方法,getMethodgetDeclaredMethod,我們就從這兩個方法開始,一步步看下反射的原理。接下來就進入程式碼分析,大家做好準備。

1.2 getMethod / getDeclaredMethod

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

class Class {      @CallerSensitive      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 = getMethod0(name, parameterTypes);          if (method == null) {              throw new NoSuchMethodException(methodToString(name, parameterTypes));          }          // 3. 返回方法的拷貝          return getReflectionFactory().copyMethod(method);      }        @CallerSensitive      public 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);      }  }    

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

  1. 檢查方法許可權
  2. 獲取方法 Method 對象
  3. 返回方法的拷貝

這裡主要有兩個區別:

  1. 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 的區別。

  1. getMethod 中獲取方法調用的是 getMethod0,而 getDeclaredMethod 獲取方法調用的是 privateGetDeclaredMethods 關於這個區別,這裡簡單提及一下,後面具體分析程式碼。privateGetDeclaredMethods 是獲取類自身定義的方法,參數是 boolean publicOnly,表示是否只獲取公共方法。
private Method[] privateGetDeclaredMethods(boolean publicOnly) {      //...  }    

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

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

1.3 getMethod 方法

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);      }  }    

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

  1. 檢查方法許可權
  2. 獲取方法 Method 對象
  3. 返回方法的拷貝

我們先看看檢查方法許可權做了些什麼事情。

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;      }  }    

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

  1. 通過 privateGetDeclaredMethods 獲取自己所有的 public 方法
  2. 通過 MethodList#filter 查找 方法名,參數相同的方法,如果找到,直接返回
  3. 如果自己沒有實現對應的方法,就去父類中查找對應的方法
  4. 查找介面中對應的方法

通過上面四個步驟,最終獲取到的是一個 MethodList 對象,是一個鏈表結點,其 next 指向下一個結點。也就是說,這裡獲取到的 Method 會有多個。這裡稍微解釋一下,在我們平時編寫 Java 程式碼時,同一個類是不能有方法名和方法參數都相同的方法的,而實際上,在 JVM 中,一個方法簽名是和 返回值,方法名,方法參數 三者相關的。也就是說,在 JVM 中,可以存在 方法名和方法參數都相同,但是返回值不同的方法。所以這裡返回的是一個方法鏈表。所以上面最終返回方法時會通過 MethodList#getMostSpecific 進行返回值的篩選,篩選出返回值類型最具體的方法。

這裡我們先暫停回顧一下整體的調用鏈路:

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 獲取方法時,有兩個步驟:

  1. relectionData 通過快取獲取
  2. 如果快取沒有命中的話,通過 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 instance          return 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 getMethods          volatile Field[] declaredPublicFields;          volatile Method[] declaredPublicMethods;          volatile Class<?>[] interfaces;            // Cached names          String simpleName;          String canonicalName;          static final String NULL_SENTINEL = new String();            // Value of classRedefinedCount when we created this ReflectionData instance          final 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 present          res.methodAccessor = methodAccessor;          return res;      }  }    

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

  1. 設置 root = this
  2. 會給 Method 設置 MethodAccessor,用於後面方法調用。也就是所有的 Method 的拷貝都會使用同一份 methodAccessor。

通過上面的步驟,就獲取到了需要反射的方法。我們再回顧一下之前的流程。

getMethod

2. 原理–調用反射方法

獲取到方法以後,通過 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. 獲取 MethodAccessor          MethodAccessor ma = methodAccessor;             // read volatile          if (ma == null) {              // 創建 MethodAccessor              ma = acquireMethodAccessor();          }          // 3. 調用 MethodAccessor.invoke          return 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 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;      }  }    

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

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

這裡可以看到,一共有三種 MethodAccessor。MethodAccessorImplNativeMethodAccessorImplDelegatingMethodAccessorImpl。採用哪種 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 版本的 MethodAccessor              MethodAccessorImpl 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 NativeConstructorAccessorImpl      private 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 bytecodes          for (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 方法反射的原理就介紹完了。

3. 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.

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

總結

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