全面詳細解析invoke方法的執行和使用

      網友投稿 1317 2022-05-30

      invoke方法

      在Java中很多方法都會調用invoke方法,很多異常的拋出多會定位到invoke方法:

      java.lang.NullPointerException at ...... at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497)

      invoke執行過程

      invoke方法用來在運行時動態地調用某個實例的方法,實現如下:

      @CallSensitive public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class caller = Refelection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor; if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); }

      AccessibleObject類是Field,Method和Constructor對象的基類:

      提供將反射的對象標記為在使用時取消默認Java語言訪問控制檢查的能力

      invoke方法會首先檢查AccessibleObject的override屬性的值:

      override默認值為false:

      表示需要權限調用規則,調用方法時需要檢查權限

      也可以使用setAccessible() 設置為true

      override如果值為true:

      表示忽略權限規則,調用方法時無需檢查權限

      也就是說,可以調用任意private方法,違反了封裝

      如果override屬性為默認值false,則進行進一步的權限檢查:

      首先用Reflection.quickCheckMemberAccess(clazz, modifiers) 方法檢查方法是否為public

      1.1 如果是public方法的話,就跳過本步

      1.2 如果不是public方法的話,就用Reflection.getCallerClass()方法獲取調用這個方法的Class對象,這是一個native方法

      @CallerSensitive public static native Class getCallerClass(); ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - 在OpenJDK中可以找到getCallerClass方法的JNI入口-Reflection.c JNIEXPORT jclass JNICALL Java_sun_reflect_Reflection_getCallerClass__ (JNIEnv *env, jclass unused) { return JVM_GetCallerClass(env, JVM_CALLER_DEPTH); } --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - JVM_GetCallerClass的源碼位于jvm.cpp中 VM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env, int depth)) JVMWrapper("JVM_GetCallerClass"); // Pre-JDK 8 and early builds of JDK 8 don't have a CallerSensitive annotation; or // sun.reflect.Reflection.getCallerClass with a depth parameter is provided // temporarily for existing code to use until a replacement API is defined. if (SystemDictionary::reflect_CallerSensitive_klass() == NULL || depth != JVM_CALLER_DEPTH) { Klass* k = thread->security_get_caller_class(depth); return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror()); } // Getting the class of the caller frame. // // The call stack at this point looks something like this: // // [0] [ @CallerSensitive public sun.reflect.Reflection.getCallerClass ] // [1] [ @CallerSensitive API.method ] // [.] [ (skipped intermediate frames) ] // [n] [ caller ] vframeStream vfst(thread); // Cf. LibraryCallKit::inline_native_Reflection_getCallerClass for (int n = 0; !vfst.at_end(); vfst.security_next(), n++) { Method* m = vfst.method(); assert(m != NULL, "sanity"); switch (n) { case 0: // This must only be called from Reflection.getCallerClass if (m->intrinsic_id() != vmIntrinsics::_getCallerClass) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "JVM_GetCallerClass must only be called from Reflection.getCallerClass"); } // fall-through case 1: // Frame 0 and 1 must be caller sensitive. if (!m->caller_sensitive()) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), err_msg("CallerSensitive annotation expected at frame %d", n)); } break; default: if (!m->is_ignored_by_security_stack_walk()) { // We have reached the desired frame; return the holder class. return (jclass) JNIHandles::make_local(env, m->method_holder()->java_mirror()); } break; } } return NULL; JVM_END

      獲取Class對象caller后使用checkAccess方法進行一次快速的權限校驗 ,checkAccess方法實現如下:

      volatile Object securityCheckCache; void checkAccess(Class caller, Class clazz, Object obj, int modifiers) throws IllegalAccessException { if(caller == clazz){ // 快速校驗 return; // 權限通過校驗 } Object cache = securityCheckCache; // 讀取volatile Class targetClass = clazz; if (obj != null && Modifier.isProtected(modifiers) && ((targetClass = obj.getClass()) != clazz)) { // 必須匹配caller,targetClass中的一個 if (cache instanceof Class[]) { Class[] cache2 = (Class[]) cache; if (cache2[1] == targetClass && cache[0] == caller) { return; // 校驗通過 } } } else if (cache == caller) { return; // 校驗通過 } slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass); }

      首先先執行一次快速校驗,一旦Class正確則權限檢查通過;如果未通過,則創建一個緩存,中間再進行檢查

      如果上面所有的權限檢查都未通過,將會執行更詳細的檢查:

      void slowCheckMemberAccess(Class caller, Class clazz, Object obj, int modifiers, Class targetClass) throws IllegalAccessException { Refelection.ensureMemberAccess(caller, clazz, obj, modifiers); // 如果成功,就更新緩存 Object cache = ((targetClass == clazz) ? caller : new Class[] {caller, targetClass}); securityCheckCache = cache; }

      用Reflection.ensureMemberAccess方法繼續檢查權限.若檢查通過就更新緩存,這樣下一次同一個類調用同一個方法時就不用執行權限檢查了,這是一種簡單的緩存機制

      由于JMM的happens-before規則能夠保證緩存初始化能夠在寫緩存之間發生,因此兩個cache不需要聲明為volatile

      檢查權限的工作到此結束.如果沒有通過檢查就會拋出異常,如果通過檢查就會到下一步

      Method.invoke() 不是自身實現反射調用邏輯,而是通過sun.refelect.MethodAccessor來處理

      Method對象的基本構成:

      每個Java方法有且只有一個Method對象作為root, 相當于根對象,對用戶不可見

      當創建Method對象時,代碼中獲得的Method對象相當于其副本或者引用

      root對象持有一個MethodAccessor對象,所有獲取到的Method對象都共享這一個MethodAccessor對象

      必須保證MethodAccessor在內存中的可見性

      root對象及其聲明:

      private volatile MethodAccessor methodAccessor; /** * For sharing of MethodAccessors. This branching structure is * currently only two levels deep (i.e., one root Method and * potentially many Method objects pointing to it.) * * If this branching structure would ever contain cycles, deadlocks can * occur in annotation code. */ private Method root;

      MethodAccessor:

      /** * This interface provides the declaration for * java.lang.reflect.Method.invoke(). Each Method object is * configured with a (possibly dynamically-generated) class which * implements this interface */ public interface MethodAccessor { // Matches specification in {@link java.lang.reflect.Method} public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException; }

      MethodAccessor是一個接口,定義了invoke() 方法,通過Usage可以看出MethodAccessor的具體實現類:

      sun.reflect.DelegatingMethodAccessorImpl

      sun.reflect.MethodAccessorImpl

      sun.reflect.NativeMethodAccessorImpl

      第一次調用Java方法對應的Method對象的invoke()方法之前,實現調用邏輯的MethodAccess對象還沒有創建

      第一次調用時,才開始創建MethodAccessor并更新為root, 然后調用MethodAccessor.invoke() 完成反射調用

      /** * NOTE that there is no synchronization used here. * It is correct(though not efficient) to generate more than one MethodAccessor for a given Method. * However, avoiding synchronization will probably make the implementation more scalable. */ 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 { tmp = reflectionFactory.newMethodAccessor(this); setMethodAccessor(tmp); } return tmp; }

      methodAccessor實例由reflectionFactory對象操控生成 ,reflectionFactory是在AccessibleObject中聲明的:

      /** * Reflection factory used by subclasses for creating field, * method, and constructor accessors. Note that this is called very early in the bootstrapping process. */ static final ReflectionFactory reflectionFactory = AccessController.doPrivileged( new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());

      sun.reflect.ReflectionFactory方法:

      public class ReflectionFactory { private static boolean initted = false; private static Permission reflectionFactoryAccessPerm = new RuntimePermission("reflectionFactoryAccess"); private static ReflectionFactory soleInstance = new ReflectionFactory(); // Provides access to package-private mechanisms in java.lang.reflect private static volatile LangReflectAccess langReflectAccess; /** * "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 noInflation = false; private static int inflationThreshold = 15; // 生成MethodAccessor 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; } } /** * We have to defer full initialization of this class until after the static initializer is run since java.lang.reflect * Method's static initializer (more properly, that for java.lang.reflect.AccessibleObject) causes this class's to be run, before the system properties are set up */ private static void checkInitted() { if (initted) return; AccessController.doPrivileged( new PrivilegedAction() { public Void run() { /** * Tests to ensure the system properties table is fully initialized * This is needed because reflection code is called very early in the initialization process (before command-line arguments have been parsed and therefore these user-settable properties installed * We assume that if System.out is non-null then the System class has been fully initialized and that the bulk of the startup code has been run */ if (System.out == null) { // java.lang.System not yet fully initialized return null; } String val = System.getProperty("sun.reflect.noInflation"); if (val != null && val.equals("true")) { noInflation = true; } val = System.getProperty("sun.reflect.inflationThreshold"); if (val != null) { try { inflationThreshold = Integer.parseInt(val); } catch (NumberFormatException e) { throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e); } } initted = true; return null; } }); } }

      實際的MethodAccessor實現有兩個版本,一個是Java版本,一個是native版本,兩者各有特點:

      初次啟動時Method.invoke() 和Constructor.newInstance() 方法采用native方法要比Java方法快3-4倍

      啟動后native方法又要消耗額外的性能而慢于Java方法

      Java實現的版本在初始化時需要較多時間,但長久來說性能較好

      這是HotSpot的優化方式帶來的性能特性:

      跨越native邊界會對優化有阻礙作用

      為了盡可能地減少性能損耗,HotSpot JDK采用inflation方式:

      Java方法在被反射調用時,開頭若干次使用native版

      等反射調用次數超過閾值時則生成一個專用的MethodAccessor實現類,生成其中的invoke() 方法的字節碼

      以后對該Java方法的反射調用就會使用Java版本

      ReflectionFactory.newMethodAccessor() 生成MethodAccessor對象的邏輯:

      native版開始會會生成NativeMethodAccessorImpl和DelegatingMethodAccessorImpl兩個對象

      DelegatingMethodAccessorImpl:

      /* Delegates its invocation to another MethodAccessorImpl and can change its delegate at run time */ 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; } }

      DelegatingMethodAccessorImpl對象是一個中間層,方便在native版與Java版的MethodAccessor之間進行切換

      native版MethodAccessor的Java方面的聲明: sun.reflect.NativeMethodAccessorImpl

      /* Used only for the first few invocations of a Method; afterward,switches to bytecode-based implementation */ 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); }

      每次NativeMethodAccessorImpl.invoke() 方法被調用時,程序調用計數器都會增加1, 看看是否超過閾值

      如果超過則調用MethodAccessorGenerator.generateMethod() 來生成Java版的MethodAccessor的實現類

      改變DelegatingMethodAccessorImpl所引用的MethodAccessor為Java版

      經由DelegatingMethodAccessorImpl.invoke() 調用到的就是Java版的實現

      JVM層invoke0方法

      invoke0方法是一個native方法,在HotSpot JVM里調用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); }

      openjdk/hotspot/src/share/vm/prims/jvm.cpp:

      JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0)) JVMWrapper("JVM_InvokeMethod"); Handle method_handle; if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) { method_handle = Handle(THREAD, JNIHandles::resolve(method)); Handle receiver(THREAD, JNIHandles::resolve(obj)); objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0))); oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL); jobject res = JNIHandles::make_local(env, result); if (JvmtiExport::should_post_vm_object_alloc()) { oop ret_type = java_lang_reflect_Method::return_type(method_handle()); assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!"); if (java_lang_Class::is_primitive(ret_type)) { // Only for primitive type vm allocates memory for java object. // See box() method. JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } } return res; } else { THROW_0(vmSymbols::java_lang_StackOverflowError()); } JVM_END

      關鍵部分為Reflection::invoke_method: openjdk/hotspot/src/share/vm/runtime/reflection.cpp

      oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) { oop mirror = java_lang_reflect_Method::clazz(method_mirror); int slot = java_lang_reflect_Method::slot(method_mirror); bool override = java_lang_reflect_Method::override(method_mirror) != 0; objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror))); oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror); BasicType rtype; if (java_lang_Class::is_primitive(return_type_mirror)) { rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL); } else { rtype = T_OBJECT; } instanceKlassHandle klass(THREAD, java_lang_Class::as_Klass(mirror)); Method* m = klass->method_with_idnum(slot); if (m == NULL) { THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke"); } methodHandle method(THREAD, m); return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD); }

      Java的對象模型 :klass和oop

      Java版的實現

      Java版MethodAccessor的生成使用MethodAccessorGenerator實現

      Generator for sun.reflect.MethodAccessor and sun.reflect.ConstructorAccessor objects using bytecodes to implement reflection. A java.lang.reflect.Method or java.lang.reflect.Constructor object can delegate its invoke or newInstance method to an accessor using native code or to one generated by this class. (Methods and Constructors were merged together in this class to ensure maximum code sharing.)

      運用了asm動態生成字節碼技術 - sun.reflect.ClassFileAssembler

      invoke總結

      invoke方法的過程:

      MagicAccessorImpl:

      原本Java的安全機制使得不同類之間不是任意信息都可見,但JDK里面專門設了個MagicAccessorImpl標記類開了個后門來允許不同類之間信息可以互相訪問,由JVM管理

      /**

      MagicAccessorImpl (named for parity with FieldAccessorImpl and others, not because it actually implements an interface) is a marker class in the hierarchy. All subclasses of this class are "magically" granted access by the VM to otherwise inaccessible fields and methods of other classes. It is used to hold the code for dynamically-generated FieldAccessorImpl and MethodAccessorImpl subclasses. (Use of the word "unsafe" was avoided in this class's name to avoid confusion with {@link sun.misc.Unsafe}.)

      The bug fix for 4486457 also necessitated disabling verification for this class and all subclasses, as opposed to just SerializationConstructorAccessorImpl and subclasses, to avoid having to indicate to the VM which of these dynamically-generated stub classes were known to be able to pass the verifier.

      Do not change the name of this class without also changing the VM's code.

      */ class MagicAccessorImpl { }

      @CallerSensitive注解

      Summary: Improve the security of the JDK’s method-handle implementation by replacing the existing hand-maintained list of caller-sensitive methods with a mechanism that accurately identifies such methods and allows their callers to be discovered reliably.

      /** * A method annotated @CallerSensitive is sensitive to its calling class, * via {@link sun.reflect.Reflection#getCallerClass Reflection.getCallerClass}, * or via some equivalent. * * @author John R. Rose */ @Retention(RetentionPolicy.RUNTIME) @Target({METHOD}) public @interface CallerSensitive { }

      用 @CallerSensitive注解修飾的方法從一開始就知道具體調用此方法的對象

      不用再經過一系列的檢查就能確定具體調用此方法的對象

      實際上是調用sun.reflect.Reflection.getCallerClass方法

      全面詳細解析invoke方法的執行和使用

      Reflection類位于調用棧中的0幀位置

      sun.reflect.Reflection.getCallerClass() 方法返回調用棧中從0幀開始的第x幀中的類實例

      該方法提供的機制可用于確定調用者類,從而實現"感知調用者(Caller Sensitive)"的行為

      即允許應用程序根據調用類或調用棧中的其它類來改變其自身的行為

      反射注意點

      反射會額外消耗系統資源,如果不需要動態地創建一個對象,就不要使用反射

      反射調用方法時可以忽略權限檢查.可能會破壞封裝性而導致安全問題

      Java JVM

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Android 上傳圖片到服務器(單文件上傳)
      下一篇:Excel連環引用法怎么轉二維表為一維表
      相關文章
      亚洲乱亚洲乱妇无码| 中文字幕亚洲精品资源网| 久久久无码精品亚洲日韩按摩 | 国产亚洲精品无码专区 | 一区二区三区亚洲| 亚洲成Av人片乱码色午夜| 国产成人亚洲综合无码精品| 国产亚洲精品成人AA片新蒲金| 综合亚洲伊人午夜网| 国产亚洲一区二区三区在线不卡 | 亚洲欧洲日本在线| 苍井空亚洲精品AA片在线播放| 亚洲高清国产拍精品熟女| 亚洲国产精品美女久久久久| 亚洲国产精品嫩草影院| 国产精品亚洲综合| 亚洲精品和日本精品| 亚洲中文字幕伊人久久无码| 国产成人亚洲精品播放器下载| 爱情岛亚洲论坛在线观看| 久久精品国产亚洲AV麻豆~| 亚洲国产精品一区二区久久hs| 亚洲国产精品无码中文字| 亚洲AV无一区二区三区久久| 亚洲av女电影网| 亚洲第一精品电影网| 亚洲av无码片区一区二区三区| 亚洲中文字幕日本无线码| 亚洲熟妇无码八V在线播放| 亚洲精品美女久久7777777| 日韩色视频一区二区三区亚洲 | 久久丫精品国产亚洲av不卡| 亚洲中文久久精品无码1| 亚洲一区二区无码偷拍| 豆国产96在线|亚洲| 亚洲综合无码AV一区二区| 久久精品国产亚洲AV网站| 亚洲网站在线免费观看| 久久亚洲国产最新网站| 国产成人精品久久亚洲高清不卡| 亚洲国产成人精品女人久久久 |