标签: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