虛擬機(jī)字節(jié)碼執(zhí)行引擎【動(dòng)態(tài)類(lèi)型語(yǔ)言支持(三)】

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í)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容