局部变量表:
方法执行时,虚拟机会把字节码中方法数据区的code类型的属性中的局部变量放到栈的局部变量表中。
操作栈:
jvm指令是基于操作栈的,也就是说,运算过程是在操作栈中进行的。
动态链接:(Dynamic Linking and Resolution):
Class字节码的常量持中存有大量的符号引用,在运行期才将符号引用变成直接引用(也就是指向数据),可以是方法或者字段的引用。
方法出口(方法返回地址):
即本方法执行后下一步指令的地址,方法正常退出时,调用者PC计数器的值就可以作为返回地址,异常退出时,返回地址是要通过异常处理器来确定。
补充:
方法调用过程在JVM中是如何表示的
package org.louis.jvm.codeset; public class Bootstrap { public static void main(String[] args) { String name = "Louis"; greeting(name); } public static void greeting(String name) { System.out.println("Hello,"+name); }
从编译到运行有以下步骤:
1. 首先JVM会先将这个Bootstrap.class 信息加载到 内存中的方法区(Method Area)中。 Bootstrap.class 中包含了常量池信息,方法的定义 以及编译后的方法实现的二进制形式的机器指令,
所有的线程共享一个方法区,从中读取方法定义和方法的指令集。 2. 接着,JVM会在Heap堆上为Bootstrap.class 创建一个Class<Bootstrap>实例用来表示Bootstrap.class 的 类实例。 3. JVM开始执行main方法,这时会为main方法创建一个栈帧,以表示main方法的整个执行过程; 4. main方法在执行的过程之中,调用了greeting静态方法,则JVM会为greeting方法创建一个栈帧,推到虚拟机栈顶。 5.当greeting方法运行完成后,则greeting方法出栈,main方法继续运行;
JVM方法调用的过程是通过栈帧来实现的。如何分配栈帧的空间,需要从字节码方法的二进制指令中得到3个信息:
1 ). 局部变量的数量(作用是:当JVM为方法创建栈帧的时候,在栈帧中为该方法创建一个局部变量表,来存储方法指令在运算时的局部变量值) 2 ). 执行时所需要的最大的操作数栈的大小(当JVM为方法创建栈帧的时候,在栈帧中为方法创建一个操作数栈,保证方法内指令可以完成工作) 3 ). 方法的参数的数量
JVM运行main方法的过程:
1.为main方法创建栈帧:
JVM解析main方法,发现其 局部变量的数量为 2,操作数栈的数量为1, 则会为main方法创建一个栈帧(VM Stack),并将其加入虚拟机栈中:
2. 完成栈帧初始化:
main栈帧创建完成后,会将栈帧push 到虚拟机栈中,现在有两步重要的事情要做:
a),将指令计数器指向main方法的地址。
计算PC值。PC 是指令计数器,其内部的值决定了JVM虚拟机下一步应该执行哪一个机器指令,而机器指令存放在方法区,我们需要让PC的值指向方法区的main方法上;
初始化 PC = main方法在方法区指令的地址+0;
b),局部变量初始化
main方法有个入参(String[] args) ,JVM已经在main所在的栈帧的局部变量表中为其空出来了一个slot ,我们需要将 args 的引用值初始化到局部点亮表中;
接着JVM开始读取PC指向的机器指令。
如上图所示,main方法的指令序列:12 10 4c 2b b8 20 12 b1
,
通过JVM虚拟机指令集规范,可以将这个指令序列解析成以下Java汇编语言:
当main方法调用greeting()时, JVM会为greeting方法创建一个栈帧,用以表示对greeting方法的调用,
具体栈帧信息如下:
具体的greeting方法的机器码表示的含义如下图所示
JVM对一个方法执行的基本策略:
重点原则:JVM的指令是基于栈的,即大部分的指令的执行,都伴随着操作数的出栈和入栈。
每个机器指令的执行,重点是对操作数栈和局部变量的影响。是读懂二进制机器指令的关键。
机器指令的格式
机器指令,就是只有机器才能够认识的二进制代码:由操作码和操作数。
JVM虚拟机的操作码是由一个字节组成的,也就是说其指令的数量最多为 2^8,即 256个;
机器指令的执行模式---基于操作数栈的模式
对于传统的物理机而言,大部分的机器指令的设计都是寄存器的,物理机内设置若干个寄存器,用以存储机器指令运行过程中的值,
寄存器的数量和支持的指令的个数决定了这个机器的处理能力。
但是Java虚拟机的设计的机制并不是这样的,Java虚拟机使用操作数栈 来存储机器指令的运算过程中的值。所有的操作数的操作,都要遵循出栈和入栈的规则
详情见《Java虚拟机规范(Java Virtual Machine Specification)》