Add a new bytecode, invokedynamic ,
that supports efficient and flexible execution of method invocations in the absence of static type information.
添加一個字節碼 invokedynamic 。用於提供在缺少靜態類型信息時高效和靈活的執行方法調用。
invokedynamic 是針對 JVM 的,用於更好的支持動態 JVM 語言的函數調用; JSR292 實現則提供在 Java 語言層面上的 invokedynamic 調用。
JSR292 實現於 JDK7,如下爲實現方案的類。java
└─java └─lang ├─invoke | ├─CallSite | ├─ConstantCallSite | ├─MethodHandle | ├─MethodHandleProxies | ├─MethodHandles | ├─MethodHandles.Lookup | ├─MethodType | ├─MutableCallSite | ├─SwitchPoint | └─VolatileCallSite ├─ClassValue └─BootstrapMethodError
JSR292 API 提供了
invokedynamic
字節碼的 API 支持。
MethodType
對象,指定方法的簽名MethodHandles.Lookup
中查找類型爲 MethodType
的 MethodHandle
MethodHandle.invoke
或者 MethodHandle.invokeExact
方法用於指定方法的類型,此處的方法類型與 Java 的方法判斷有點區別,Java 的方法簽名不包括 返回值 ,可是 MethodType
倒是經過 返回值 和 參數類型 來肯定。而且該類型不包含 方法名 。bootstrap
// 快速建立一個 MethodType 。 MethodType methodType = MethodType.methodType(String.class, Object.class); // 也能夠經過 MethodHandle 實例獲取它要調用的方法實例 // MethodHandle handle = ... MethodType type = handle.type();
除了這兩種獲取 MethodType 的方法(實際上第二種不算,MethodHandle 對象的建立依賴於 MethodType)。下面還有兩種,它也是 MethodType
的靜態方法:多線程
genericMethodType
: 全部類型都是 Object 類型fromMethodDescriptorString
: 有兩個參數,一個是方法描述符,該描述符是基於 字節碼 層面的描述符。以後就是一個類加載器。固然,建立完以後還能夠修改返回值類型和參數類型如: changeParameterType
、 changeReturnType
等。函數
一個工廠類,用於建立 MethodHandle
類對象。它可經過普通的 findXxx() 方法獲得相應的 MethodHandle
實例,也能夠經過 反射類 來建立實例。如 lookup.unreflect(Method)
等方法。工具
MethodHandle
可當作是方法引用,它使得 Java 擁有了相似函數指針或委託的方法別名工具。學習
注意幾點:flex
MethodHandle
的 type 保持一致。MethodHandle
也是可執行及能夠進行轉換。幾個 MethodHandle
方法與字節碼的對應:優化
MethodHandle方法 | 字節碼 | 描述 |
---|---|---|
findStatic | invokestatic | 調用靜態方法 |
findSpecial | invokespecial | 調用實例構造方法,私有方法,父類方法。 |
findVirtual | invokevirtual | 調用全部的虛方法 |
findVirtual | invokeinterface | 調用接口方法,會在運行時再肯定一個實現此接口的對象。 |
示例ui
public void example() throws Throwable { // MethodHandles.Lookup 相似於 MethodHandle 工廠類,用於建立 MethodHandle MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle mh = lookup.findStatic(String.class, "valueOf", MethodType.methodType(String.class, int.class)); String result = (String) mh.invoke(1); System.out.println("1".equals(result)); }
三種調用方式:.net
invokeExact
: 調用此方法與直接調用底層方法同樣,須要作到參數類型精確匹配invoke
: 參數類型鬆散匹配,經過asType自動適配invokeWithArguments
: 直接經過方法參數來調用當 JVM 執行 invokedynamic
指令時,首先須要連接其對應的 動態調用點 。在連接的時候,JVM會先調用一個啓動方法(bootstrap method
)。這個啓動方法的返回值是 java.lang.invoke.CallSite
類的對象。
在經過啓動方法獲得了 CallSite 以後,經過這個 CallSite 對象的 getTarget()
能夠獲取到實際要調用的目標方法句柄。
有了方法句柄以後,對這個 動態調用點 的調用,其實是代理給方法句柄來完成的。
也就是說,對 invokedynamic
指令的調用實際上就等價於對方法句柄的調用,具體來講是被轉換成對方法句柄的invoke方法的調用。
JDK7 中提供了三種類型的動態調用點CallSite的實現:java.lang.invoke.ConstantCallSite
、java.lang.invoke.MutableCallSite
和 java.lang.invoke.VolatileCallSite
。
表示的調用點綁定的是一個固定的方法句柄,一旦連接以後,就沒法修改。示例以下:
public void constantCallSite() throws Throwable { MethodType type = MethodType.methodType(String.class, int.class, int.class); MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle handle = lookup.findVirtual(String.class, "substring", type); ConstantCallSite callSite = new ConstantCallSite(handle); MethodHandle invoker = callSite.dynamicInvoker(); String result = (String) invoker.invoke("Hello", 2, 3); System.out.println(result); }
表示的調用點則容許在運行時動態修改其目標方法句柄,便可以從新連接到新的方法句柄上。示例以下:
/** * MutableCallSite 容許對其所關聯的目標方法句柄經過setTarget方法來進行修改。 * 如下爲 建立一個 MutableCallSite,指定了方法句柄的類型,則設置的其餘方法也必須是這種類型。 */ public void useMutableCallSite() throws Throwable { MethodType type = MethodType.methodType(int.class, int.class, int.class); MutableCallSite callSite = new MutableCallSite(type); MethodHandle invoker = callSite.dynamicInvoker(); MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle maxHandle = lookup.findStatic(Math.class, "max", type); callSite.setTarget(maxHandle); int result = (int) invoker.invoke(3, 5); System.out.println(result == 5); MethodHandle minHandle = lookup.findStatic(Math.class, "min", type); callSite.setTarget(minHandle); result = (int) invoker.invoke(3, 5); System.out.println(result == 3); }
MutableCallSite.syncAll()
提供了方法來強制要求各個線程中 MutableCallSite
的使用者當即獲取最新的目標方法句柄。
但這個時候也能夠選擇使用 VolatileCallSite
。
做用與 MutableCallSite 相似,不一樣的是它適用於多線程狀況,用來保證對於目標方法句柄所作的修改可以被其餘線程看到。
此處便再也不提供示例,可參考 MutableCallSite
。
字節碼
,而 Reflection 則不用考慮這些。