JAVA深入研究——Method的Invoke方法
public class A {
public void foo(String name) {
System.out.println("Hello, " + name);
}
}
可以編寫另外一個類來反射呼叫A上的方法:
import java.lang.reflect.Method;
public class TestClassLoad {
public static void main(String[] args) throws Exception {
Class<?> clz = Class.forName("A");
Object o = clz.newInstance();
Method m = clz.getMethod("foo", String.class);
for (int i = 0; i < 16; i++) {
m.invoke(o, Integer.toString(i));
}
}
}
注意到TestClassLoad類上不會有對類A的符號依賴——也就是說在載入並初始化TestClassLoad類時不需要關心類A的存在與否,而是等到main()方法執行到呼叫Class.forName()時才試圖對類A做動態載入;這裡用的是一個引數版的forName(),也就是使用當前方法所在類的ClassLoader來載入,並且初始化新載入的類。……好吧這個細節跟主題沒啥關係。
回到主題。這次我的測試環境是Sun的JDK 1.6.0 update 13 build 03。編譯上述程式碼,並在執行TestClassLoad時加入-XX:+TraceClassLoading引數(或者-verbose:class或者直接-verbose都行),如下:
[Loaded TestClassLoad from file:/D:/temp_code/test_java_classload/] [Loaded A from file:/D:/temp_code/test_java_classload/] [Loaded sun.reflect.NativeMethodAccessorImpl from shared objects file] [Loaded sun.reflect.DelegatingMethodAccessorImpl from shared objects file] Hello,0 Hello, 1 Hello, 2 Hello, 3 Hello, 4 Hello, 5 Hello, 6 Hello, 7 Hello, 8 Hello, 9 Hello, 10 Hello, 11 Hello, 12 Hello, 13 Hello, 14 [Loaded sun.reflect.ClassFileConstants from shared objects file] [Loaded sun.reflect.AccessorGenerator from shared objects file] [Loaded sun.reflect.MethodAccessorGenerator from shared objects file] [Loaded sun.reflect.ByteVectorFactory from shared objects file] [Loaded sun.reflect.ByteVector from shared objects file] [Loaded sun.reflect.ByteVectorImpl from shared objects file] [Loaded sun.reflect.ClassFileAssembler from shared objects file] [Loaded sun.reflect.UTF8 from shared objects file] [Loaded java.lang.Void from shared objects file] [Loaded sun.reflect.Label from shared objects file] [Loaded sun.reflect.Label$PatchInfo from shared objects file] [Loaded java.util.AbstractList$Itr from shared objects file] [Loaded sun.reflect.MethodAccessorGenerator$1 from shared objects file] [Loaded sun.reflect.ClassDefiner from shared objects file] [Loaded sun.reflect.ClassDefiner$1 from shared objects file] [Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__] Hello, 15
可以看到前15次反射呼叫A.foo()方法並沒有什麼稀奇的地方,但在第16次反射呼叫時似乎有什麼東西被觸發了,導致JVM新載入了一堆類,其中就包括[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]這麼一行。這是哪裡來的呢?
先來看看JDK裡Method.invoke()是怎麼實現的。
java.lang.reflect.Method:
public final class Method extends AccessibleObject implements GenericDeclaration, Member { // ... 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.) private Method root; // ... public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class caller = Reflection.getCallerClass(1); Class targetClass = ((obj == null || !Modifier.isProtected(modifiers)) ? clazz : obj.getClass()); boolean cached; synchronized (this) { cached = (securityCheckCache == caller) && (securityCheckTargetClassCache == targetClass); } if (!cached) { Reflection.ensureMemberAccess(caller, clazz, obj, modifiers); synchronized (this) { securityCheckCache = caller; securityCheckTargetClassCache = targetClass; } } } } if (methodAccessor == null) acquireMethodAccessor(); return methodAccessor.invoke(obj, args); } // 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 void 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; return; } // Otherwise fabricate one and propagate it up to the root tmp = reflectionFactory.newMethodAccessor(this); setMethodAccessor(tmp); } // ... }
可以看到Method.invoke()實際上並不是自己實現的反射呼叫邏輯,而是委託給sun.reflect.MethodAccessor來處理。
每個實際的Java方法只有一個對應的Method物件作為root,。這個root是不會暴露給使用者的,而是每次在通過反射獲取Method物件時新建立Method物件把root包裝起來再給使用者。在第一次呼叫一個實際Java方法對應得Method物件的invoke()方法之前,實現呼叫邏輯的MethodAccessor物件還沒建立;等第一次呼叫時才新建立MethodAccessor並更新給root,然後呼叫MethodAccessor.invoke()真正完成反射呼叫。
那麼MethodAccessor是啥呢?
sun.reflect.MethodAccessor:
public interface MethodAccessor { /** Matches specification in {@link java.lang.reflect.Method} */ public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException; }
可以看到它只是一個單方法介面,其invoke()方法與Method.invoke()的對應。
建立MethodAccessor例項的是ReflectionFactory。
sun.reflect.ReflectionFactory:
public class ReflectionFactory { private static boolean initted = false; // ... // // "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; private static int inflationThreshold = 15; // ... /** 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 Object 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 (RuntimeException) new RuntimeException("Unable to parse property sun.reflect.inflationThreshold"). initCause(e); } } initted = true; return null; } }); } // ... public MethodAccessor newMethodAccessor(Method method) { checkInitted(); if (noInflation) { 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實現有兩個版本,一個是Java實現的,另一個是native code實現的。Java實現的版本在初始化時需要較多時間,但長久來說效能較好;native版本正好相反,啟動時相對較快,但執行時間長了之後速度就比不過Java版了。這是HotSpot的優化方式帶來的效能特性,同時也是許多虛擬機器的共同點:跨越native邊界會對優化有阻礙作用,它就像個黑箱一樣讓虛擬機器難以分析也將其內聯,於是執行時間長了之後反而是託管版本的程式碼更快些。
為了權衡兩個版本的效能,Sun的JDK使用了“inflation”的技巧:讓Java方法在被反射呼叫時,開頭若干次使用native版,等反射呼叫次數超過閾值時則生成一個專用的MethodAccessor實現類,生成其中的invoke()方法的位元組碼,以後對該Java方法的反射呼叫就會使用Java版。
Sun的JDK是從1.4系開始採用這種優化的。
PS.可以在啟動命令里加上-Dsun.reflect.noInflation=true,就會RefactionFactory的noInflation屬性就變成true了,這樣不用等到15呼叫後,程式一開始就會用java版的MethodAccessor了。
上面看到了ReflectionFactory.newMethodAccessor()生產MethodAccessor的邏輯,在“開頭若干次”時用到的DelegatingMethodAccessorImpl程式碼如下:
sun.reflect.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; } }
這是一個間接層,方便在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 { if (++numInvocations > ReflectionFactory.inflationThreshold()) { 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()方法被呼叫時,都會增加一個呼叫次數計數器,看超過閾值沒有;一旦超過,則呼叫MethodAccessorGenerator.generateMethod()來生成Java版的MethodAccessor的實現類,並且改變DelegatingMethodAccessorImpl所引用的MethodAccessor為Java版。後續經由DelegatingMethodAccessorImpl.invoke()呼叫到的就是Java版的實現了。
注意到關鍵的invoke0()方法是個native方法。它在HotSpot VM裡是由JVM_InvokeMethod()函式所支援的:
由C編寫
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); }
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():
// This would be nicer if, say, java.lang.reflect.Method was a subclass // of java.lang.reflect.Constructor 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_klassOop(mirror)); methodOop 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); }
再下去就深入到HotSpot VM的內部了,本文就在這裡打住吧。有同學有興趣深究的話以後可以再寫一篇討論native版的實現。
回到Java的一側。MethodAccessorGenerator長啥樣呢?由於程式碼太長,這裡就不完整貼了,有興趣的可以到OpenJDK 6的Mercurial倉庫看:OpenJDK 6 build 17的MethodAccessorGenerator。它的基本工作就是在記憶體裡生成新的專用Java類,並將其載入。就貼這麼一個方法:
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 {相關推薦
Java 深入研究 Method 的 Invoke 方法
在寫程式碼的時候,發現從父類class通過getDeclaredMethod獲取的Method可以呼叫子類的物件,而子類改寫了這個方法,從子類class通過getDeclaredMethod也能獲取到Method,這時去呼叫父類的物件也會報錯。雖然這是很符合多型的現象,也符合java的動態繫結規範,
JAVA深入研究——Method的Invoke方法。
總結 一個方法可以生成多個Method物件,但只有一個root物件,主要用於持有一個MethodAccessor物件,這個物件也可以認為一個方法只有一個,相當於是static的。因為Method的invoke是交給MethodAccessor執行的,所以我所想要知道的答案在MethodAccessor的i
JAVA深入研究——Method的Invoke方法
Method的invoke方法1.先檢查 AccessibleObject的override屬性是否為true。AccessibleObject是Method,Field,Constructor的父類,override屬性預設為false,可呼叫setAccessible方法改變,如果設定為true,則表示可
【Java深入研究】2、LinkedList源碼解析
hand exception second 處的 err 所有 one tee string 一、源碼解析 1、 LinkedList類定義。 public class LinkedList<E> extends AbstractSe
深入研究java.lang.Object類
下一個 line 版本號 gin bool 獲得 不同 ava 表達 前言:Java的類庫日益龐大。所包括的類和接口也不計其數。但當中有一些非常重要的類和接口,是Java類庫中的核心部分。常見的有String、Object、Class、Collection、Class
【轉載】對一致性Hash算法,Java代碼實現的深入研究
困難 之前 存在 itl ger 正常 我不 操作 算法實現 原文地址:http://www.cnblogs.com/xrq730/p/5186728.html 一致性Hash算法 關於一致性Hash算法,在我之前的博文中已經有多次提到了,MemCache超詳細解讀一
對一致性Hash算法,Java代碼實現的深入研究
memcach 還原 情況 () 實用 target 強人 最壞情況 一致性hash 一致性Hash算法 關於一致性Hash算法,在我之前的博文中已經有多次提到了,MemCache超詳細解讀一文中"一致性Hash算法"部分,對於為什麽要使用一致性Hash算法、一致性Has
【JavaNIO的深入研究4】內存映射文件I/O,大文件讀寫操作,Java nio之MappedByteBuffer,高效文件/內存映射
int start lib 交換文件 bsp 沒有 res collected str time 內存映射文件能讓你創建和修改那些因為太大而無法放入內存的文件。有了內存映射文件,你就可以認為文件已經全部讀進了內存,然後把它當成一個非常大的數組來訪問。這種解決辦法能大大簡化修
《Java編程思想》筆記 第十七章 容器深入研究
let 類庫 font 有關 有一個 www. 強引用 容器類 刪除 1 容器分類 容器分為Collection集合類,和Map鍵值對類2種 使用最多的就是第三層的容器類,其實在第三層之上還有一層Abstract 抽象類,如果要實現自己的集合類,可以繼承Abstract類
深入研究Java String
開始寫 Java 一年來,一直都是遇到什麼問題再去解決,還沒有主動的深入的去學習過 Java 語言的特性和深入閱讀 JDK 的原始碼。既然決定今後靠 Java吃飯,還是得花些心思在上面,放棄一些打遊戲的時間,系統深入的去學習。 Java String 是 Java
深入研究java.lang.ThreadLocal類
ThreadLocal是什麼呢?其實ThreadLocal並非是一個執行緒的本地實現版本,它並不是一個Thread,而是threadlocalvariable(執行緒區域性變數)。也許把它命名為ThreadLocalVar更加合適。執行緒區域性變數(Threa
Java String的深入研究以及intern()原理
When---什麼時候需要了解String的intern方法: 面試的時候(蜜汁尷尬)!雖然不想承認,不過面試的時候經常碰到這種高逼格的問題來考察我們是否真正理解了String的不可變性、String常量池的設計以及String.intern方法所做的事情。但其實,我們
《Java設計模式深入研究》介紹
《Java設計模式深入研究》共分12章,首先強調了介面和抽象類在設計模式中的重要性,介紹了反射技術在設計模式中的應用。然後,從常用的23個設計模式中精選10個進行了詳細的講解,包括2個建立型模式、4個行為型模式、4個結構型模式。《Java設計模式深入研究》理論講解透徹,應用示例深入。設計模式的講解均從生
Java程式設計思想 第十七章:深入研究容器
1. 完整的容器分類法 下面是集合類庫的完整圖: Java SE5新添加了: Queue介面(LinkedList已經為實現該介面做了修改)及其實現PriorityQueue和各種風格的BlockingQueue。 ConcurrentMap介面及其實現Concu
Java-Java程式設計思想第四版 第十七章 容器深入研究 練習
練習1:/* Create a List (try both ArrayList and LinkedList) and fill it using * Countries. Sort the list and print it, then apply Collections
JAVA序列化機制的深入研究
1、java序列化簡介 序列化就是指物件通過寫出描述自己狀態的數值來記錄自己的過程,即將物件表示成一系列有序位元組,java提供了將物件寫入流和從流中恢復物件的方法。物件能包含其它的物件,而其它的物件又可以包含另外的物件。JAVA序列化能夠自動的處理巢狀的物
對一致性Hash演算法,Java程式碼實現的深入研究
1 /** 2 * 帶虛擬節點的一致性Hash演算法 3 * @author 五月的倉頡 http://www.cnblogs.com/xrq730/ 4 */ 5 public class ConsistentHashingWithVirtualNode 6 { 7
【Java NIO的深入研究6】JAVA NIO之Scatter-Gather
Java NIO開始支援scatter/gather,scatter/gather用於描述從Channel(譯者注:Channel在中文經常翻譯為通道)中讀取或者寫入到Channel的操作。 分散(scatter)從Channel中讀取是指在讀操作時將讀取的資
Java反射機制深入研究
Java反射是Java語言的一個很重要的特徵,它使得Java具體了“動態性”。 在Java執行時環境中,對於任意一個類,能否知道這個類有哪些屬性和方法?對於任意一個物件,能否呼叫它的任意一個方法?答案是肯定的。這種動態獲取類的資訊以及動態呼叫物件的方法的功能來自於Java語言的反射(Reflection
Java容器深入研究(jdk 1.8)--- ArrayList總結與原始碼分析
結構:public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializabl