JVM笔记整理(第8章 虚拟机字节码执行引擎)

来源:互联网 发布:网络转串口 编辑:程序博客网 时间:2024/06/09 22:10

资料来源《深入理解java虚拟机》

 

整理到这这一章的时候,越来越觉得这本书在明年的春招前多看多理解几遍。内容真的很好很多,也是为以后的技术之路做好准备。

 

前一章讲解了类加载机制。那么把字节码加载到虚拟机以后,JVM该如何去执行呢?这就是这章要讲的内容。所谓执行字节码,其实就是执行程序中的一个个和业务相关的方法。我们都知道,每个java方法的执行,其实都对应着一个栈帧从入栈到出栈的过程。因此,要知道方法如何执行,就必须要了解栈帧的存储结构,这样才能了解到这个栈帧是如何对应着一个方法的执行的。这就是本章第一个重要的部分:栈帧结构。接下来要从JVM角度分析,针对一个调用某方法的字节码指令, JVM是如何找到这个方法,把其栈帧放入栈中的几种查找方法版本的机制。这是本章第二个比较重要的地方。前面两个部分都是讲的JVM是如何调用方法的内容的,那本章最后一节讲的是如何执行方法中的字节码指令的。这样整个执行过程:从字节码加载到JVM,到输出结果,就全了。

 

 

1、一些模糊的概念

1.1、执行引擎到底是什么?

 执行字节码指令的部分。

1.2、执行引擎完成的工作是什么?

         把输入JVM的字节码,经过处理,输出执行结果。故每个java方法的执行,都对应着一个栈帧从入栈到出栈的过程。

1.3、执行引擎工作时的两种工作方式?

            1.3.1、解释执行:通过解释器执行。

            1.3.2、编译执行:通过即时编译器产生本地代码再执行。

 

 

 

2、栈帧结构

栈帧结构:<局部变量表、操作数栈、动态连接、方法返回地址、其他额外信息>

注1:通常,把动态连接、方法返回地址、其他额外信息,这三者统称为:栈帧信息。

 

2.1、局部变量表

2.1.1、定义:是方法参数和方法中定义的局部变量的存储空间。

2.1.2、大小确定时间:程序编译时,存在于Code属性表中max_locals数据项中。

2.1.3、分配单位:变量槽(slot):32位bit以内。

注1:java虚拟机数据类型和java数据类型是有本质差别的。

注2:每个slot都能存放一个boolean、byte、char、short、int、short、float、double、reference、returnaddress类型的数据。

注3:reference表示对一个实例对象的引用。通过reference,JVM至少可以做到两点:a、从过此引用直接or间接的在java堆中查找到对象的起始地址索引。b、利用此引用直接or间接的找到对象所属类型在方法区中存储的类型信息。

注4: returnaddress现在已经不再使用。

注5:对于64位bit数据long和double,需要使用2个slot来存储。读取时,需要分2此读取。因为局部常量表在虚拟机栈中,是线程私有,故两次读取不论是否是原子操作,都不会引起线程安全问题。

2.1.4、工作方式:JVM通过索引定位方式,使用局部变量表。索引从0开始,到slot最大数量。

2.1.5、如何分配局部变量表?先按照参数表顺序,分配参数;再顺序分配局部变量。

2.1.6、功能:方法执行时,JVM使用局部变量表,完成参数值(存在于局部变量表)到参数变量列表的传递过程。

2.1.7、局部变量是否自动赋值?局部变量和类变量不同,没有“准备阶段”,所以没有想当然的自动赋值阶段。所以局部变量在使用时必须先手动赋值。

 

 

2.2操作数栈

2.2.1、深度确定时间:在编译时,写入Code属性的max_stacks数据项中。

2.2.2、32位栈容量为1,64为栈容量为2。

2.2.3、数据类型一致:当前执行的字节码指令中的数据类型和要和当前方法中操作数的数据类型对应。在编译代码、类校验阶段都会检查这一点。

 

 

2.3、动态连接

2.3.1、存储内容:栈帧包含的运行时常量池中该帧所属方法的引用,

2.3.2、存在意义:是为了支持方法调用过程中的动态连接。

2.3.3、字节码的方法调用指令:以常量池中方法的符号引用为参数。动态连接是指:方法在每一次执行时,将符号引用转化为直接引用。静态调用:指在类加载阶段或者第一次使用的时候就转化为直接引用。

 

 

2.4、方法返回地址

2.4.1、两类返回方式:程序正常结束和异常结束

2.4.2、退出后可能的操作:a、恢复上层方法的局部变量表和操作数栈。b、把返回值压入调用者栈帧的操作数栈。c、调整pc计数器的值指向方法调用指令后面的一条指令。

 

 

 

3、方法调用

3.1、实质:确定被调用方法的版本。这里很容易混淆的概念就是:方法调用和方法执行是两回事。方法执行是执行具体的java方法代码;方法调用是根据字节码方法调用指令,去查找要被执行的是哪个方法,这就是方法的版本。

 

3.2、从字节码到方法调用到底是一个怎样的过程?

首先,需要明确的一点是:.class文件没有连接过程,也就没有需要被执行代码在内存中的入口地址,它仅仅存储了被执行方法的符号引用。JVM根据字节码中方法的符号引用,去方法区中的常量池中查找对应的符号引用,将带有该符号引用的栈帧入栈,有了真正的内存入口地址后,就完成了常量池中的符号引用到直接引用的转化,继而开始执行java方法。

 

3.3、方法调用的2种方式

注1:此处,要知道的两个知识点:每种调用方法对应的对象是什么;重载和重写在JVM中是如何确定目标方法的。

 

3.3.1、解析

3.3.1.1、什么样的方法调用称为“解析”?

调用目标在程序写好,编译器进行编译时就必须确定下来。这类方法的调用称为“解析”。

3.3.1.2、使用对象:所有非虚方法的调用。非虚方法包括:静态方法、私有方法、构造器方法、父类方法、被final修饰的方法。

3.3.1.3、发生时间:类加载的解析阶段,确定方法调用版本。

3.3.1.4、为什么非虚方法可以在类加载解析阶段确定方法调用版本?

因为这些方法,在程序运行前就是一个确定的方法调用版本。并且在程序运行期间方法调用版本不会改变。

3.3.1.5、非虚方法在字节码指令中对应的指令:

invokestatic  :调用静态方法

invokespecial :调用实例构造器方法、私有方法、父类方法。

注意:final方法是被invokevirtual字节码指令调用的。

3.3.1.6、解析调用:一定是静态的过程。

 

3.3.2、分派

3.3.2.1、分类:

根据分派依据的宗量数目,分为:单分派、多分派;

根据是依据静态类型or实际类型来分派方法执行版本,分为:静态分派、动态分派;

根据以上因素综合,分为:静态单分派、静态多分派、动态单分派、动态多分派。

注1:宗量:值java方法中的调用者和方法参数。

Eg:Man  man=new Man();           man.sayhello(“hello”);  //这里的man和hello都是宗量。前者为调用者,后者为方法的参数。

3.3.2.2、静态分派

A、实现原理:JVM根据参数的静态类型确定执行方法的哪个版本。

B、应用:java中的重载

注1:参数的静态类型在编译时期就确定了,但参数的实际类型需要在运行时才能确定。

C、分派动作执行者:javac编译器,而非虚拟机。

D、代码例子:

 

/** * Created by cxh  on 17/07/21. */public class Main {    static abstract class Human{};    static class Man extends Human{};    static class Woman extends Human{};    public void sayHello(Human guy){        System.out.println("hello,guy!");    }    public void sayHello(Man man){        System.out.println("hello,gentleman!");    }    public void sayHello(Woman woman){        System.out.println("hello,lady!");    }    public static void main(String[] args) {        Human man=new Man(); //man静态类型为Human        Human woman=new Woman();//woman静态类型为Human        Main test=new Main();        test.sayHello(man);        test.sayHello(woman);    }}

运行结果:

hello,guy!hello,guy!Process finished with exit code 0


3.3.2.3、动态分派:

A、应用:java中的重写

B、分派动作执行者:java虚拟机

C、实现原理:invokevirtual指令的多态查找过程。

3.3.2.4、静态分派属于多分派类型、动态分派属于单分派类型

 

4、字节码解释执行引擎

这一部分就不总结了,内容比较少。需要注意的就是,只有确定了谈论对象是某种确定的java实现版本和执行引擎运行模式后,谈解释执行还是编译执行才会比较确切。

原创粉丝点击