java7 invokedynamic學習筆記

概述

jvm中方法調用指令有:

invokeinterface:調用接口方法;

invokespecial:專門用來調用父類方法、私有方法和初始化方法;

invokestatic:調用靜態方法;

invokevirtual:調用對象的一般方法。

這四個指令所對應的類、調用的方法在編譯時幾乎是固定的:invokestatic所對應的類爲靜態方法所在的類,方法爲靜態方法本身;invokespecial所對應的類爲當前對象,方法是固定的;invokeinterface和invokevirtual所對應的類也爲當前對象,方法可以因爲繼承和實現進行選擇,但也僅限於整個繼承體系中選擇(比如:一個類的equals方法有重寫,則調用當前對象的equals方法,否則就調用Object的equals方法,選擇的餘地不大)。


invokedynamic

在java7 JVM中增加了一個新的指令invokedynamic,用於支持動態語言,即允許方法調用可以在運行時指定類和方法,不必在編譯的時候確定。字節碼中每條invokedynamic指令出現的位置稱爲一個動態調用點,invokedynamic指令後面會跟一個指向常量池的調用點限定符,這個限定符會被解析爲一個動態調用點。解析和調用過程如下:

1、根據invokedynamic指令後面的限定符#n,找到調用點限定符在常量池中的位置,調用點限定符的符號引用爲CONSTANT_InvokeDynamic_info結構:

CONSTANT_InvokeDynamic_info{
	u1 tag;
	u2 bootstrap_method_attr_index; 
	u2 name_and_type_index;
}

2、通過CONSTANT_InvokeDynamic_info結構,找到引導方法,引導方法返回值必須是java.lang.invoke.CallSite類型

3、調用引導方法。和調用普通方法一樣

動態調用點限定符的符號引用解析時出現了異常、或者引導方法執行出現異常、或者引導方法的返回值不匹配、MethodHandle方法描述不一致等都會拋出BootstrapMethodError異常。

4、執行完引導方法之後,動態調用點會返回一個調用點對象(CallSite)、此對象將會與動態調用點永久綁定,隨後調用點對象的MethodHandle將會被調用,即調用invoke、invokeExact等方法。

實例

本實例通過asm 5.0.4來生成帶invokedynamic的字節碼。

1、引導方法
package invokedynamic.demo;
import java.lang.invoke.*;

public class Bootstrap {
    private static void hello() {
        System.out.println("Hello!");
    }

    public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        Class thisClass = lookup.lookupClass();
        MethodHandle mh = lookup.findStatic(thisClass, "hello", MethodType.methodType(void.class));
        return new ConstantCallSite(mh.asType(type));
    }
}
Bootstrap類中包含引導方法bootstrap,返回值爲CallSite且有固定的參數。引導方法中創建了一個MethodHandle,對引導方法的調用會調用MethodHandle底層的方法(Bootstrap.hello())。
2、invokedynamic字節碼生成
public byte[] generate(String dynamicInvokeClassName, String dynamicLinkageClassName, String bootstrapMethodName, String methodDescriptor)
		throws Exception {
	ClassWriter cw = new ClassWriter(0);
	MethodVisitor mv;

	cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, dynamicInvokeClassName, null, "java/lang/Object", null);
	{
		mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
		mv.visitCode();
		MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
				MethodType.class);
		Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, dynamicLinkageClassName, bootstrapMethodName,
				mt.toMethodDescriptorString());
		mv.visitInvokeDynamicInsn("dynamicInvoke", methodDescriptor, bootstrap);
		mv.visitInsn(RETURN);
		mv.visitMaxs(0, 1);
		mv.visitEnd();
	}
	cw.visitEnd();

	return cw.toByteArray();
}
public static void main(String[] args) throws IOException, Exception {
        String dynamicClass = "invokedynamic/demo/DynamicInvoker";
        FileOutputStream fos = new FileOutputStream(new File("d:/code/java7/out/production/java7/" + dynamicClass + ".class"));
        fos.write(new DynamicInvokerGenerator().generate(dynamicClass, "invokedynamic/demo/Bootstrap", "bootstrap", "()V"));
}
重點看這句:mv.visitInvokeDynamicInsn("dynamicInvoke", methodDescriptor, bootstrap) 在字節代碼中生成invokedynamic指令,在調用的時候傳入了方法的名稱dynamicInvoke和對應的啓動方法bootstrap。
執行main函數後生成了DynamicInvoker.class文件,用javap命令查看字節碼如下:
public class invokedynamic.demo.DynamicInvoker
  BootstrapMethods:
    0: #13 invokestatic invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      Method arguments:
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Utf8               invokedynamic/demo/DynamicInvoker
   #2 = Class              #1             //  invokedynamic/demo/DynamicInvoker
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             //  java/lang/Object
   #5 = Utf8               main
   #6 = Utf8               ([Ljava/lang/String;)V
   #7 = Utf8               invokedynamic/demo/Bootstrap
   #8 = Class              #7             //  invokedynamic/demo/Bootstrap
   #9 = Utf8               bootstrap
  #10 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #11 = NameAndType        #9:#10         //  bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #12 = Methodref          #8.#11         //  invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #13 = MethodHandle       #6:#12         //  invokestatic invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/
  #14 = Utf8               dynamicInvoke
  #15 = Utf8               ()V
  #16 = NameAndType        #14:#15        //  dynamicInvoke:()V
  #17 = InvokeDynamic      #0:#16         //  #0:dynamicInvoke:()V
  #18 = Utf8               Code
  #19 = Utf8               BootstrapMethods
{
  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: invokedynamic #17,  0             // InvokeDynamic #0:dynamicInvoke:()V
         5: return
}
第33行生成了ynvokedynamic指定,動態調用點在常量池#17,也就是25行,第3行是對引導方法的描述。
用java命令執行這個class文件,打印出Hello!
D:\code\java7\out\production\java7>java invokedynamic.demo.DynamicInvoker
Hello!

示例代碼下載地址:http://download.csdn.net/detail/aesop_wubo/9154407
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章