java7 invokedynamic学习笔记

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结构:

 

[html]  view plain  copy
 
 print ?
  1. CONSTANT_InvokeDynamic_info{  
  2.     u1 tag;  
  3.     u2 bootstrap_method_attr_index;   
  4.     u2 name_and_type_index;  
  5. }  

 

2、通过CONSTANT_InvokeDynamic_info结构,找到引导方法,引导方法返回值必须是Java.lang.invoke.CallSite类型

3、调用引导方法。和调用普通方法一样

 

动态调用点限定符的符号引用解析时出现了异常、或者引导方法执行出现异常、或者引导方法的返回值不匹配、MethodHandle方法描述不一致等都会抛出BootstrapMethodError异常。

4、执行完引导方法之后,动态调用点会返回一个调用点对象(CallSite)、此对象将会与动态调用点永久绑定,随后调用点对象的MethodHandle将会被调用,即调用invoke、invokeExact等方法。
 

实例

本实例通过asm 5.0.4来生成带invokedynamic的字节码。
 
1、引导方法
[java]  view plain  copy
 
 print ?
  1. package invokedynamic.demo;  
  2. import java.lang.invoke.*;  
  3.   
  4. public class Bootstrap {  
  5.     private static void hello() {  
  6.         System.out.println("Hello!");  
  7.     }  
  8.   
  9.     public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {  
  10.         MethodHandles.Lookup lookup = MethodHandles.lookup();  
  11.         Class thisClass = lookup.lookupClass();  
  12.         MethodHandle mh = lookup.findStatic(thisClass, "hello", MethodType.methodType(void.class));  
  13.         return new ConstantCallSite(mh.asType(type));  
  14.     }  
  15. }  
Bootstrap类中包含引导方法bootstrap,返回值为CallSite且有固定的参数。引导方法中创建了一个MethodHandle,对引导方法的调用会调用MethodHandle底层的方法(Bootstrap.hello())。
2、invokedynamic字节码生成
[java]  view plain  copy
 
 print ?
  1. public byte[] generate(String dynamicInvokeClassName, String dynamicLinkageClassName, String bootstrapMethodName, String methodDescriptor)  
  2.         throws Exception {  
  3.     ClassWriter cw = new ClassWriter(0);  
  4.     MethodVisitor mv;  
  5.   
  6.     cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, dynamicInvokeClassName, null"java/lang/Object"null);  
  7.     {  
  8.         mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main""([Ljava/lang/String;)V"nullnull);  
  9.         mv.visitCode();  
  10.         MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,  
  11.                 MethodType.class);  
  12.         Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, dynamicLinkageClassName, bootstrapMethodName,  
  13.                 mt.toMethodDescriptorString());  
  14.         mv.visitInvokeDynamicInsn("dynamicInvoke", methodDescriptor, bootstrap);  
  15.         mv.visitInsn(RETURN);  
  16.         mv.visitMaxs(01);  
  17.         mv.visitEnd();  
  18.     }  
  19.     cw.visitEnd();  
  20.   
  21.     return cw.toByteArray();  
  22. }  
[java]  view plain  copy
 
 print ?
  1. public static void main(String[] args) throws IOException, Exception {  
  2.         String dynamicClass = "invokedynamic/demo/DynamicInvoker";  
  3.         FileOutputStream fos = new FileOutputStream(new File("d:/code/java7/out/production/java7/" + dynamicClass + ".class"));  
  4.         fos.write(new DynamicInvokerGenerator().generate(dynamicClass, "invokedynamic/demo/Bootstrap""bootstrap""()V"));  
  5. }  
重点看这句:mv.visitInvokeDynamicInsn("dynamicInvoke", methodDescriptor, bootstrap) 在字节代码中生成invokedynamic指令,在调用的时候传入了方法的名称dynamicInvoke和对应的启动方法bootstrap。
执行main函数后生成了DynamicInvoker.class文件,用javap命令查看字节码如下:
[java]  view plain  copy
 
 print ?
  1. public class invokedynamic.demo.DynamicInvoker  
  2.   BootstrapMethods:  
  3.     0: #13 invokestatic invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;  
  4.       Method arguments:  
  5.   minor version: 0  
  6.   major version: 51  
  7.   flags: ACC_PUBLIC, ACC_SUPER  
  8. Constant pool:  
  9.    #1 = Utf8               invokedynamic/demo/DynamicInvoker  
  10.    #2 = Class              #1             //  invokedynamic/demo/DynamicInvoker  
  11.    #3 = Utf8               java/lang/Object  
  12.    #4 = Class              #3             //  java/lang/Object  
  13.    #5 = Utf8               main  
  14.    #6 = Utf8               ([Ljava/lang/String;)V  
  15.    #7 = Utf8               invokedynamic/demo/Bootstrap  
  16.    #8 = Class              #7             //  invokedynamic/demo/Bootstrap  
  17.    #9 = Utf8               bootstrap  
  18.   #10 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;  
  19.   #11 = NameAndType        #9:#10         //  bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;  
  20.   #12 = Methodref          #8.#11         //  invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;  
  21.   #13 = MethodHandle       #6:#12         //  invokestatic invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/  
  22.   #14 = Utf8               dynamicInvoke  
  23.   #15 = Utf8               ()V  
  24.   #16 = NameAndType        #14:#15        //  dynamicInvoke:()V  
  25.   #17 = InvokeDynamic      #0:#16         //  #0:dynamicInvoke:()V  
  26.   #18 = Utf8               Code  
  27.   #19 = Utf8               BootstrapMethods  
  28. {  
  29.   public static void main(java.lang.String[]);  
  30.     flags: ACC_PUBLIC, ACC_STATIC  
  31.     Code:  
  32.       stack=0, locals=1, args_size=1  
  33.          0: invokedynamic #17,  0             // InvokeDynamic #0:dynamicInvoke:()V  
  34.          5return  
  35. }  
第33行生成了ynvokedynamic指定,动态调用点在常量池#17,也就是25行,第3行是对引导方法的描述。
用java命令执行这个class文件,打印出Hello!
[html]  view plain  copy
 
 print ?
  1. D:\code\java7\out\production\java7>java invokedynamic.demo.DynamicInvoker  
  2. Hello!  
示例代码下载地址:http://download.csdn.net/detail/aesop_wubo/9154407

猜你喜欢

转载自gelongmei.iteye.com/blog/2361075
今日推荐