标签:jvm invokedynamic 动态 方法调用 jdk7
在JDK7中,Java提供了对动态语言特性的支持,实现了JSR 292 《Supporting Dynamically Typed Languages on the Java Platform》规范,这是Java语言发展的一重大进步,而提供对动态语言特性支持也是Java发展的一大趋势与方向。那么动态性表现在哪里呢?其一在Java API层面,新增了java.lang.invoke包,主要包含了CallSite、MethodHandle、MethodType等类;其二,在Java字节码指令层面,新增了invokedynamic指令,而伴随invokedynamic指令新增而在Class类文件常量池中新增了CONSTANT_InvokeDynamic_info, CONSTANT_MethodHandle_info, CONSTANT_MethodType_info常量表、新增BootstrapMethods属性表。function Output() { } Output.prototype.println = function(_value) { console.info(_value);//FireFox中 } //执行语句 var output = new Output(); output.println();
public static void main(String[] args) { System.out.println("HelloWorld"); }
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.Random; public class Output { public void println(Object value) { System.out.println("value=" + value); } public static MethodHandle getMethodHandle(Object receiver) throws Throwable { //如果Lookup对象 MethodHandles.Lookup lookup = MethodHandles.lookup(); //MethodType代表方法的类型(不包含方法名称),其实MethodType是为了确定方法的描述符,例如此方法描述符为:(Ljava/lang/Object;)V MethodType methodType = MethodType.methodType(void.class, Object.class); //在接收者类中查找一个名为println,指定方法类型的虚方法 return lookup.findVirtual(receiver.getClass(), "println", methodType).bindTo(receiver); } public static void main(String[] args) throws Throwable { Object receiver = new Random().nextInt(1000)%2==0 ? System.out : new Output(); // 无论receiver最终是什么类型,只要有println方法,方法就可以正常调用。 getMethodHandle(receiver).invoke("Hello Dynamic Invoke"); } }
1. Reflection和MethodHandle机制本质上都是在模拟方法调用,但是Reflection是在模拟Java代码层次的方法调用,而MethodHandle是在模拟字节码层次的方法调用。在MethodHandles.Lookup上的三个方法findStatic()、findVirtual()、findSpecial()正是为了对应于invokestatic、invokevirtual & invokeinterface和invokespecial这几条字节码指令的执行权限校验行为,而这些底层细节在使用Reflection API时是不需要关心的。
2. Reflection中的java.lang.reflect.Method对象远比MethodHandle机制中的java.lang.invoke.MethodHandle对象所包含的信息来得多。前者是方法在Java一端的全面映像,包含了方法的签名、描述符以及方法属性表中各种属性的Java端表示方式,还包含有执行权限等的运行期信息。而后者仅仅包含着与执行该方法相关的信息。用开发人员通俗的话来讲,Reflection是重量级,而MethodHandle是轻量级。
public static java.lang.invoke.MethodHandle getMethodHandle(java.lang.Object) throws java.lang.Throwable; flags: ACC_PUBLIC, ACC_STATIC Exceptions: throws java.lang.Throwable Code: stack=4, locals=3, args_size=1 0: invokestatic #48 // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; 3: astore_1 4: getstatic #54 // Field java/lang/Void.TYPE:Ljava/lang/Class; 7: ldc #3 // class java/lang/Object 9: invokestatic #60 // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType; 12: astore_2 13: aload_1 14: aload_0 15: invokevirtual #66 // Method java/lang/Object.getClass:()Ljava/lang/Class; 18: ldc #70 // String println 20: aload_2 21: invokevirtual #71 // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; 24: aload_0 25: invokevirtual #77 // Method java/lang/invoke/MethodHandle.bindTo:(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; 28: areturn LineNumberTable: line 16: 0 line 18: 4 line 20: 13 LocalVariableTable: Start Length Slot Name Signature 0 29 0 receiver Ljava/lang/Object; 4 25 1 lookup Ljava/lang/invoke/MethodHandles$Lookup; 13 16 2 methodType Ljava/lang/invoke/MethodType; public static void main(java.lang.String[]) throws java.lang.Throwable; flags: ACC_PUBLIC, ACC_STATIC Exceptions: throws java.lang.Throwable Code: stack=2, locals=2, args_size=1 0: new #87 // class java/util/Random 3: dup 4: invokespecial #89 // Method java/util/Random."<init>":()V 7: sipush 1000 10: invokevirtual #90 // Method java/util/Random.nextInt:(I)I 13: iconst_2 14: irem 15: ifne 24 18: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream; 21: goto 31 24: new #1 // class com/xtayfjpk/asm/test/dynamicinvoke/demo/Output 27: dup 28: invokespecial #94 // Method "<init>":()V 31: astore_1 32: aload_1 33: invokestatic #95 // Method getMethodHandle:(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; 36: ldc #97 // String Hello Dynamic Invoke 38: invokevirtual #99 // Method java/lang/invoke/MethodHandle.invoke:(Ljava/lang/String;)V 41: return LineNumberTable: line 26: 0 line 28: 32 line 29: 41 LocalVariableTable: Start Length Slot Name Signature 0 42 0 args [Ljava/lang/String; 32 10 1 receiver Ljava/lang/Object; StackMapTable: number_of_entries = 2 frame_type = 24 /* same */ frame_type = 70 /* same_locals_1_stack_item */ stack = [ class java/lang/Object ]
import static org.objectweb.asm.Opcodes.*; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.invoke.MethodHandles.Lookup; import java.nio.file.Files; import java.nio.file.Paths; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Handle; import org.objectweb.asm.MethodVisitor; public class DynamicInvokeInstructionGenerator { //启动方法定义 public static CallSite bootstrap(Lookup lookup, String name, MethodType type, String value) throws Exception { MethodHandle handle = lookup.findVirtual(StringBuilder.class, name, MethodType.methodType(StringBuilder.class)).bindTo(new StringBuilder(value)); return new ConstantCallSite(handle); } //ASM中定义的方法句柄 private static final Handle BSM = new Handle( H_INVOKESTATIC, DynamicInvokeInstructionGenerator.class.getName().replace('.', '/'), "bootstrap", MethodType.methodType(CallSite.class, Lookup.class, String.class, MethodType.class, String.class).toMethodDescriptorString()); public static void main(String[] args) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(V1_7, ACC_PUBLIC|ACC_SUPER, "StringReverser", null, "java/lang/Object", null); //生成main方法 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC|ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); //调用StringBuilder的reverse方法 mv.visitInvokeDynamicInsn("reverse", "()Ljava/lang/StringBuilder;", BSM, "Hello Dynamic Invoke");//生成invokedynamic指令 //调用System.out.println(Object x) mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V"); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); cw.visitEnd(); Files.write(Paths.get("StringReverser.class"), cw.toByteArray()); } }
只是为了书写方便,就把启动方法,定义方法句柄,生成字节码的代码写在一个类中了,对生成的StringReverser类执行javap命令后得到如果结果:
{ public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream; 3: invokedynamic #25, 0 // InvokeDynamic #0:reverse:()Ljava/lang/StringBuilder; 8: invokevirtual #31 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 11: return }
至此手动生成字节码成功并顺序执行。
关于启动方法签名,java.lang.invoke包中类的更详细信息可参看:http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/package-summary.html
标签:jvm invokedynamic 动态 方法调用 jdk7
原文地址:http://blog.csdn.net/xtayfjpk/article/details/42043977