概述
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
- }
- D:\code\java7\out\production\java7>java invokedynamic.demo.DynamicInvoker
- Hello!