java invokedynamic虚拟机指令
阅读数:90 评论数:0
跳转到新版页面分类
python/Java
正文
一、概述
new | 创建一个对象并将地址放入虚拟机栈 |
---|---|
dup | 复制一个对象地址放入虚拟机栈 |
invokespecial | 用于调用构造方法、私有方法及final方法 |
invokevirtual | 用于调用普通的需要动态加载的方法 |
invokestatic | 用于调用静态方法 |
invokeinterface | 用于调用接口方法 |
checkcast | 确定对象为所给定的类型并强制类型转换 |
putstatic | 设置类中静态字段的值 |
getstatic | 从类中获取静态字段 |
putfield | 设置对象中字段的值 |
getfield | 从对象中获取字段 |
instanceof | 判断对象是否为给定的类型 |
pop | 将上面执行的最近的栈顶数值弹出栈 |
istore_0 | 将上面执行最近的引用地址放入局部变量表第零个槽位(相应的 i 可以替换为s,l,f,d,a,ia,ba,sa,la,fa,da,ca,aa,分别指代int,short,long,float,double,引用类型,int数组,boolean数组,short数组,long数组,float数组,double数组,char数组,引用类型数组) |
iload_1 | 将局部变量表中第一个槽位的值或地址放入虚拟机栈(相应的 i 可以替换为s,l,f,d,a,ia,ba,sa,la,fa,da,ca,aa,分别指代int,short,long,float,double,引用类型,int数组,boolean数组,short数组,long数组,float数组,double数组,char数组,引用类型数组) |
iconst_1 | 当int取值-1~5时,取一个常量放入虚拟机栈(相应的前面的i:int类型,可替换为除了byte的其它几种基本数据类型)。 |
bipush | int取值-128~127时, 认为是一个byte类型的值放入虚拟机栈 |
sipush | 当int取值-32768~32767时,认为是short类型的值放入虚拟机栈。 |
ldc | 用于将int、float、String类型常量从常量池中压到虚拟机栈 |
goto | 跳转到某行指令 |
return | 返回方法命令 |
相应的i可以替换为s、l、f、d,分别指代int、short、long、float、double。
iadd | int类型的加法 |
---|---|
isub | int类型的减法 |
imul | int类型的乘法 |
idiv | int类型的除法 |
irem | int类型的除法的余数 |
ineg | int类型的取反操作 |
iinc | int类型的本身加上一个常量 |
相应的i可以替换为s、l、f、d,分别指代int、short、long、float、double。
ifeq | 是否等于0 |
---|---|
ifne | 是否不等于0 |
iflt | 是否小于0 |
ifge | 是否大于等于0 |
ifgt | 是否大于0 |
ifle | 是否小于等于0 |
if_icmpeq | 判断两个值是否相等 |
if_icmpne | 判断两个值是否不相等 |
if_icmplt | 判断先入栈的是否小于后入栈的 |
if_icmple | 判断先入栈的是否小于等于后入栈的 |
if_icmpge | 判断先入栈的是否大于等于后入栈的 |
if_icmpgt | 判断先入栈的是否大于后入栈的 |
ifnull | 判断是否为null |
ifnonnull | 判断是否不为null |
lcmp | 比较两个值long类型值 |
---|---|
fcmpl | 比较float类型值(当遇到NaN时,返回-1) |
fcmpg | 比较float类型值(当遇到NaN时,返回1) |
dcmpl | 比较double类型值(当遇到NaN时,返回-1) |
dcmpg | 比较double类型值(当遇到NaN时,返回1) |
invokedynamic是java 7引入的一条虚拟机指令,到java 8后被应用到lambda表达式中。invokedynamic与其它invoke指令不同的是它允许应用级的代码来决定方法解析。这个应用级代码被称为引导方法(Bootstrap Method),简称BSM。BSM返回一个CallSite对象,CallSite就是一个MethodHandle的holder,这个MethodHandle指向真正要执行的方法,这个CallSite和invokedynamic链接在一起,以后再执行这条invokedynamic指令都不会创建新的CallSite对象。
二、MethodHandle
通过MethodHandle可以调用method、construct、field,比起反射更强大的是,Method Handle还可以调用到父类的方法。
class Example {
void doSomething() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
}
private void foo() { /* ... */ }
}
MethodHandle不能直接初始化,可以使用MethodHandles类提供的工厂方法,lookup对象仅仅能定位到Example类可见的属性,比如说foo方法,其它类中对Exampe类不可见的私有方法是定位不到的。
一个Method handle只能持有一个指定具体类型的方法,方法的类型包括返回类型和参数类型。
可以使用MethodType来描述方法的类型。
class Counter {
static int count(String name) {
return name.length();
}
}
MethodType methodType = MethodType.methodType(int.class, new Class<?>[] {String.class});
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle methodHandle = lookup.findStatic(Counter.class, "count", methodType);
int count = methodHandle.invokeExact("foo");
assertThat(count, is(3));
通过上面创建的lookup和methodType,可以用来定位Counter类的count方法。
Java中的每个方法都有一个独特的方法签名,签名由方法名、方法参数组成。虽然语言层面上不允许通过改变方法返回类型来进行方法的重载,但是字节码层面是允许的。
public class Bean {
String value;
void print(String x) {
System.out.println(x);
}
}
MethodHandle fieldHandle = lookup.findSetter(Bean.class, "value", String.class);
MethodType methodType = MethodType.methodType(void.class, new Class<?>[] {String.class});
MethodHandle methodHandle = lookup.findVirtual(Bean.class, "print", methodType);
使用Methods.Lookup对象,可以获取对变量的引用。如果想要设置一个变量可以使用findSetter方法,想要读取一个变量,可以使用findGetter方法。
(1)Reflection和MethodHandle机制本质上都是在模拟方法调用,但是Refelction是在模拟Java代码层次的方法调用,而MethodHandle是在模拟字节码层次的方法调用。
在MethodHandles.Lookup上的三个方法findStatic()、findVirtual()、findSpecial()正是为了对应于invokestatic、invokevirtual & invokeinterface和invokespecial这几条字节码指令的执行权限校验行为,而这些底层细节在使用Reflection API时是不需要关心的。
(2)java.lang.reflect.Method对象远比MethodHandle机制中的java.lang.invoke.MethodHandle对象所包含的信息来得多。前者是方法在Java一端的全面映像,包含了方法的签名、描述符以及方法属性表中各种属性的Java端表示方式,还包含有执行权限等的运行期信息。而后者仅仅包含着与执行该方法相关的信息。