概述
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異常。
實例
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));
}
}
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"));
}
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行是對引導方法的描述。D:\code\java7\out\production\java7>java invokedynamic.demo.DynamicInvoker
Hello!