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

你可能感兴趣的:(java7 invokedynamic学习笔记)