invokedynamic指令
invokedynamic指令與MethodHandle機(jī)制的作用是一樣的,都是為了解決原有4條“invoke*”指令方法分派規(guī)則完全固化在虛擬機(jī)之中的問(wèn)題,把如何查找目標(biāo)方法的決定權(quán)從虛擬機(jī)轉(zhuǎn)嫁到具體用戶(hù)代碼之中,讓用戶(hù)(廣義的用戶(hù),包含其他程序語(yǔ)言的設(shè)計(jì)者)有更高的自由度。而且,它們兩者的思路也是可類(lèi)比的,都是為了達(dá)到同一個(gè)目的,只是一個(gè)用上層代碼和API來(lái)實(shí)現(xiàn),另一個(gè)用字節(jié)碼和Class中其他屬性、常量來(lái)完成。
每一處含有invokedynamic指令的位置都被稱(chēng)作“動(dòng)態(tài)調(diào)用點(diǎn)(Dynamically-Computed Call Site)”,這條指令的第一個(gè)參數(shù)不再是代表方法符號(hào)引用的CONSTANT_Methodref_info常量,而是變?yōu)镴DK7時(shí)新加入的CONSTANT_InvokeDynamic_info常量,從這個(gè)新常量中可以得到3項(xiàng)信息:引導(dǎo)方法(Bootstrap Method,該方法存放在新增的BootstrapMethods屬性中)、方法類(lèi)型(MethodType)和名稱(chēng)。引導(dǎo)方法是有固定的參數(shù),并且返回值規(guī)定是java.lang.invoke.CallSite對(duì)象,這個(gè)對(duì)象代表了真正要執(zhí)行的目標(biāo)方法調(diào)用。根據(jù)CONSTANT_InvokeDynamic_info常量中提供的信息,虛擬機(jī)可以找到并且執(zhí)行引導(dǎo)方法,從而獲得一個(gè)CallSite對(duì)象,最終調(diào)用到要執(zhí)行的目標(biāo)方法上。
InvokeDynamic指令演示:
package com.test;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class InvokeDynamicTest {
public static void main(String[] args) throws Throwable {
INDY_BootstrapMethod().invokeExact("icyfenix");
}
public static void testMethod(String s){
System.out.println("hello String:" + s );
}
public static CallSite BootstrapMethod(MethodHandles.Lookup lookup,
String name,MethodType mt) throws Throwable{
return new ConstantCallSite(
lookup.findStatic(InvokeDynamicTest.class, name, mt));
}
private static MethodType MT_BootstrapMethod(){
return MethodType
.fromMethodDescriptorString(
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;"
+ "Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", null);
}
private static MethodHandle MH_BootstrapMethod() throws Throwable {
return MethodHandles.lookup().findStatic(InvokeDynamicTest.class,
"BootstrapMethod",
MT_BootstrapMethod());
}
private static MethodHandle INDY_BootstrapMethod() throws Throwable {
CallSite cs = (CallSite)MH_BootstrapMethod().invokeWithArguments(
MethodHandles.lookup(),
"testMethod",
MethodType.fromMethodDescriptorString("(Ljava/lang/String;)V", null));
return cs.dynamicInvoker();
}
}
由于invokedynamic指令面向的主要服務(wù)對(duì)象并非Java語(yǔ)言,而是其他Java虛擬機(jī)之上的其他動(dòng)態(tài)類(lèi)型語(yǔ)言,因此,光靠Java語(yǔ)言的編譯器Javac的話(huà),在JDK7時(shí)甚至還完全沒(méi)有辦法生成帶有invokedynamic指令的字節(jié)碼(曾經(jīng)有一個(gè)java.dyn.InvokeDynamic的語(yǔ)法糖可以實(shí)現(xiàn),后來(lái)別取笑了),而到了JDK8引入了Lambda表達(dá)式和接口默認(rèn)方法后,Java語(yǔ)言才算享受到了一點(diǎn)invokedynamic指令的好處,但用Lambda來(lái)解釋invokedynamic指令運(yùn)作就比較別扭,也無(wú)法與前面的MethodHandle的例子對(duì)應(yīng)類(lèi)比,所以采用變通的辦法:John Rose(JSR 292的負(fù)責(zé)人)編寫(xiě)過(guò)一個(gè)把程序字節(jié)碼轉(zhuǎn)換為使用invokedynamic的簡(jiǎn)單工具INDY來(lái)完成這件事,使用這個(gè)工具來(lái)產(chǎn)生最終需要的字節(jié)碼,因此上述代碼中的方法名稱(chēng)不能隨意改動(dòng),更不能把幾個(gè)方法合并到一起寫(xiě),因?yàn)樗鼈兪且籌NDY工具讀取的。
《深入理解Java虛擬機(jī)》第三版學(xué)習(xí)