java字节码编程技术(2/10)-高级指令
  TEZNKK3IfmPf 2024年03月29日 47 0

invokestatic 静态方法调用

编译期间确定,且运行期间不会修改,这类调用不需要把对象加载到操作数栈里,只需要将所需要的参数入栈就可以操作了,效率比较高;

invokevirtual普通方法调用

运行期间确定,原因是受继承等因素影响编译期间无法确定,这类调用需要把对象引用、参数加载到操作数栈里。如果方法有返回则会把返回值压入栈顶。

invokespecial特殊方法调用

编译期间确定,原因是一般不会被改写:适用于1、构造器<init>;2、使用super关键字调用父类的方法3、private私有方法调用。

invokeinterface接口方法调用

运行期间确定,每个类在运行时都有一个vtable和itable

vtable虚方法表

在方法调用时是通过索引找到方法引用的。比如A类有123三个方法,B extends A,复写了方法2,同时新增了方法4。那么结构如下,这里需要注意的是子类会保留父类中方法的索引顺序,比如下面的方法2,在所有子类的索引顺序全是2。即子类会继承父类的vtable。这样JVM才会很方便的通过索引来调用

index

方法引用

index

方法引用

1

A/method1

1

A/method1

2

A/method2

2

B/method2

3

A/method3

3

A/method3

   

4

B/method4

itable接口方法表

它由偏移量和方法表组成,在调用接口方法时,JVM会在偏移量表中查找到对应的方法表位置和方法位置,然后在方法表中查找具体的方法实现。

invokedynamic动态方法调用

这个指令是在1.8中才有的。这个指令有一套对应的API,其作用就是把固化到JVM中的上述几个指令开放给程序员来调用;

public class Foo {

public void print(String s){

System.out.println("hello "+ s);

}

 

public static void main(String []args) throws Throwable {

Foo foo = new Foo();

//定义方法签名,参数内容为返回值和入参

MethodType methodType = MethodType.methodType(void.class, String.class);

/**返回方法句柄

* MethodHandles.lookup()返回一个上下文,通过find指令查找不同的方法,找到的用MethodType签名的方法称为

句柄 */

MethodHandle methodHandle = MethodHandles.lookup().findVirtual(Foo.class, "print", methodType);

// hello world

methodHandle.invokeExact(foo, "world");

}

}

二、特殊用法

泛型

在使用时确定,在编译时检查,其目的就是为了节省代码量。但存在很多兼容问题。因为泛型在编译成字节码后就不存在泛型信息(类型擦除)了。所以在使用时需要格外小心。下面是它的缺点:

1、泛型没有自己的.class对象,所以不存在List<Integer>.class,因为类型擦除后就是List.class;

2、泛型不能是基础类型,因为编译擦除后变变为Object类型,而int类型是不能存到Object中的;所以不存在List<int>.class

3、不能定义异常类型的异常,因为异常是在编译时存放在异常表中,如果不能确定类型就不能正常编译,比如pubic <T extends Throwable> foo()就是错误的定义;

4、不能定义为数组,因为Pair<Integer>[],在编译后就变成了Pair [],即Object可以存放任何类型,造成了不安全;

通过下表可以出这两种写法是一样的

public class Pair<T> {

public T left;

}

 

Pair<String> pair = new Pair<>();

String s = pair.left;

aload 1

getfield 'com/jvm//Pair.left','Ljava/lang/Object;'

checkcast 'java/lang/String'

astore 2

class Pair1{

public Object left;

}

Pair1 pair1 = new Pair1();

s = (String)pair1.left;

aload 3

getfield 'com/jvm/Pair1.left','Ljava/lang/Object;'

checkcast 'java/lang/String'

astore 2

反射

在java中Method.invoke调用的是MethodAccessor接口来实现反射的,此接口有两个实现类,在程序反射调用过程中都会使用,由两个参数控制:

sun.reflect.inflationThreshold=15以及sun.reflect.noInflation=false。当小于15次调用时则采用JNI方式即NativeMethodAccessorImpl,当调用次数大于15时则采用GeneratedMethodAcessor这是一种ASM实现。后者是前者效率的20倍,适用于少量调用;当大量调用时适合用后者,但后者第一次生成字节码时效率比JNI低5倍左右。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2024年03月29日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   20天前   46   0   0 java
TEZNKK3IfmPf