《Java虚拟机原理图解》2.JVM机器指令集,
《Java虚拟机原理图解》2.JVM机器指令集,
前言
Java虚拟机和真实的计算机一样,运行的都是二进制的机器码;而我们将.java 源代码编译成.class 文件,class文件便是Java虚拟机能够认识的二进制机器码,Java能够识别class文件中的信息和机器指令,进而执行这些机器指令。那么,Java虚拟机是如何运行这些二进制的机器码的呢? 本文将通过一个非常简单的例子,带你感受一下Java虚拟机运行机器码的过程和其工作的基本原理。
读完本文,你将会了解到:
1、Java虚拟机对运行时虚拟机栈(JVM Stack) 的组织
2、方法调用过程是怎样在JVM中表示的
3、JVM对一个方法执行的基本策略
4. JVM机器指令的格式
5. 机器指令的执行模式—基于操作数栈的模式
上述的描述可能会有点抽象,为了给读者一个直观的感受,我们定义一个简单的Java类,然后执行这个运行这个类,逐步分析整个Java虚拟机的运行时信息的组织的。
package org.louis.jvm.codeset;
/**
* JVM 原理简单用例
* @author louis
*
*/
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);
}
}
当我们将Bootstrap.java 编译成Bootstrap.class 并运行这段程序的时候,在JVM复杂的运行逻辑中,会有以下几步:
5.当greeting方法运行完成后,则greeting方法出栈,main方法继续运行;
JVM方法调用的过程是通过栈帧来实现的,那么,方法的指令是如何运行的呢?弄清楚这个之前,我们要先了解对于JVM而言,方法的结构是什么样的。
我们知道,class 文件时 JVM能够识别的二进制文件,其中通过特定的结构描述了每个方法的定义。
JVM在编译Bootstrap.java 的过程中,在将源代码编译成二进制机器码的同时,会判断其中的每一个方法的三个信息:
1 ). 在运行时会使用到的局部变量的数量(作用是:当JVM为方法创建栈帧的时候,在栈帧中为该方法创建一个局部变量表,来存储方法指令在运算时的局部变量值)
2 ). 其机器指令执行时所需要的最大的操作数栈的大小(当JVM为方法创建栈帧的时候,在栈帧中为方法创建一个操作数栈,保证方法内指令可以完成工作)
3 ). 方法的参数的数量
经过编译之后,我们可以得到main方法和greeting方法的信息如下:
JVM运行main方法的过程:
1.为main方法创建栈帧:
JVM解析main方法,发现其 局部变量的数量为 2,操作数栈的数量为1, 则会为main方法创建一个栈帧(VM Stack),并将其加入虚拟机栈中:
接着JVM开始读取PC指向的机器指令。如上图所示,main方法的指令序列:12 10 4c 2b b8 20 12 b1 ,通过JVM虚拟机指令集规范,可以将这个指令序列解析成以下Java汇编语言:
机器指令->汇编语言->解释->对栈帧的影响
0x12 0x10 -> ldc #16 ->将常量池中第16个常量池项引用推到操作数栈栈顶。常量池第16项是CONSTANT_UTF-8_INFO项,表示”Louis”字符串->
0x4c->astore_1->操作数栈的栈顶元素出栈,将栈顶元素的值赋给index=1 的局部变量表元素上。这里等价于:name = “Louis”.->
0x2b->aload_1->将局部变量表中index=1的元素的值推到操作数栈栈顶->
0xb8 0x20 0x12->invokestatic #18->
0xb8表示机器指令invokestatic,操作数是0x20 << 8| 0x12 = 18,操作数18表示指向常量池第18项,该项是main方法的符号引用:org/louis/jvm/codeset/Bootstrap.greeting:(Ljava/lang/String;)V 当JVM执行这条语句的时候,会做以下几件事:
a).方法符号引用校验。会校验这个方法的符号引用,按照这个符号规则 在常量池中查找是否有这个方法的定义,如果找到了此方法的定义,则表示解析成功。如果是方法greeting:(Ljava/lang/String;)V没有找到,JVM会抛出错误NoSuchMethodError
b).为新的方法调用创建新的栈帧。然后JVM会为此方法greeting创建一个新的栈帧(VM stack),并根据greeting中操作数栈的大小和局部变量的数量分别创建相应大小的操作数栈;然后将此栈帧推到虚拟机栈的栈顶。
c).更新PC指令计数器的值。将当前PC程序计数器的值记录到greeting栈帧中,当greeting执行完成后,以便恢复PC值。更新PC的值,使下一条执行的指令地址指向greeting方法的指令开始部分。
这条语句会使当前的main方法执行暂停,使JVM进入对greeting方法的执行当中当greeting方法执行完成后,才会恢复PC程序计数器的值指向当前下一条指令。
0xb1->return->返回
当main方法调用greeting()时, JVM会为greeting方法创建一个栈帧,用以表示对greeting方法的调用,具体栈帧信息如下:
注:
a). 如上图所示JVM虚拟机的操作码是由一个字节组成的,也就是说对于JVM虚拟机而言,其指令的数量最多为 2^8,即 256个;
b). 上图中的操作码如:b2,bb,59….等等都是表示某一特定的机器指令,为了方便我们识别,其分别有相应的助记符:getstatic,new,dup…. 这样方便我们理解。
相关文章
- 暂无相关文章
用户点评