jvm - 指令

一 4种方法调用的区别

java提供了两类基本的方法:

实例方法和类(静态)方法,区别:

(1)实例方法在被调用之前,需要一个实例,而类方法不用;

(2)实例方法使用动态绑定,而类方法使用静态绑定。

 

<一>从这几个方面考虑:常量池池入口类型、调用的方法的类型、操作数栈中的内容、从常量池获取的东西

1 invokespecial    常量池入口类型CONST_Methodref_info; 运行时按照对象的类来调用实例方法;超类方法(使用super关键字调用的方法)、私有方法、<init>方法,但不可以是<clinit>;栈中n-1个方法参数和objectref引用

2 invokeinterface 常量池入口类型CONST_InterfaceMethodref_info;静态方法-不可以是<init>或<clinit>;栈中n-1个方法参数和objectref引用

3 invokevirtual     常量池入口类型CONST_Methodref_info;不可以是<init>或<clinit>;栈中n-1个方法参数和objectref引用

4 invokestatic      常量池入口类型CONST_Methodref_info;调用静态方法;不可以是<init>或<clinit>;栈中n个方法参数

 

<二>invokevirtual和invokeinterface的区别:都需要获取此对象的实例方法的引用,然后在方法表中调用这个方法。

invokevirtual 为objectref所指的对象的类定位方法表,(从常量池入口获取的方法的表索引index和参数个数)访问index位置的直接引用。

invokeinterface

(1)定位方法表(2)在其中搜索是否有与所解析的方法的名称和描述符匹配的方法 (3)从接口调用一个方法,必须搜索对象的类的方法表来找到一个合适的方法。

 

invokeinterface比invokevirtual慢的原因

(1)invokevirtual 在常量池入口解析得到在方法表中的偏移量,根据偏移量定位方法。

        invokeinterface 在常量池入口解析得到方法的名字和描述符,根据名字和描述符搜索整个方法表

(2)_quick技术,使用quik技术把invokevirtual操作码替换成invokevirtual_quik指令,虚拟机还把方法表偏移量和参数的个数放入每个invokevirtual指令后面的两个操作字节中;在操作码后面的字节流中直接使用方法表偏移量,节约了在解析过常量池入口中查找偏移量的时间。

 

<三>invokevirtual和invokespecial的区别:

invokespecial在编译时选择要调用的方法,它的选择是基于引用类型的(静态绑定) ACC_SUPER例外:在子类调用超类方法的时候发生。

invokevirtual在运行期间选择要调用的方法,它的选择是基于对象类型的(动态绑定)

 

public class Null { public static void greet(){ System.out.println("Hello World!"); } public static void main(String[] args) { Null x = null; x.greet(); ((Null)x).greet(); ((Null)null).greet(); } }

 

三条greet方法调用均会打印 “Hello World”,分析原因,使用javap -verbose 命令显示编译后的指令码,使用三条invokestatic指令完成操作。

 

public static void main(java.lang.String[]); Code: Stack=1, Locals=2, Args_size=1 0: aconst_null 1: astore_1 2: invokestatic #31; //Method greet:()V 5: invokestatic #31; //Method greet:()V 8: invokestatic #31; //Method greet:()V 11: return LineNumberTable: line 28: 0 line 29: 2 line 30: 5 line 31: 8 line 32: 11 LocalVariableTable: Start Length Slot Name Signature 0 12 0 args [Ljava/lang/String; 2 10 1 x Lnihao/dlut/Null;

 

 二 栈和局部变量操作

   1 把常量压入操作数栈、执行通用栈操作、在局部变量和操作数栈之间传递值。

   2 常量入栈操作数来源:隐含在操作码内部、在字节流中如同操作数紧随操作码、常量池取;

   3 wide指令

 

三 浮点运算

 一个浮点数有四部分组成:符号 位数 指数 基数

 规范化浮点数:针对尾数来说,它的小数点左边的数一定是0,紧接小数点右边的的数字一定不是0;

 这一部分没有看懂:最大、最小、NaN、+0,-0、正负无穷大的二进制表示??

 

四 对象和数组

new : 新建一个实例,用默认值初始化对象实例变量、把新对象的引用压入栈。

 

 

五 控制流

lookupswitch和tableswitch的区别?

lookupswitch 需要逐个比较case中的值,才能确认;搜索完毕没有找到匹配项,执行默认程序分支偏移量;

tableswitch 先比较高值偏移量和低值偏移量,键值在他们之间的时候,用键值减去低值,得到分支偏移量列表中的偏移量。

 

六 异常

继承自Error和RuntimeException的异常都是unchecked,其他都是checked,需要方法处理(catch或者throws)

jvm - 指令_第1张图片

 

 

   异常表中的每个入口都包括4部分信息:

   起点

   终点

   将要跳转到的字节码序列中的pc指针偏移量

   被捕获的异常类的常量池索引。

 

 七 finally 

 

------------------------------------------------------------------

参考《inside JVM 2e 》

 

你可能感兴趣的:(java,jvm,虚拟机,String,null,Class)