第5章 指令集和解释器

Java虚拟机顾名思义,就是一台虚拟的机器,而字节码(bytecode)就是运行在这台虚拟机器上的机器码
每一个类或者接口都会被Java编译器编译成一个class文件,类或接口的方法信息就放在class文件的method_info结构中 。
如果方法不是抽象的,也不是本地方法,方法的Java代码就会被编译器编译成字节码(即使方法是空的,编译器也会生成一条return语句),存放在method_info结构的Code属性中。

package jvmgo.book.ch03;

public class ClassFileTest {
    
    public static final boolean FLAG = true;
    public static final byte BYTE = 123;
    public static final char X = 'X';
    public static final short SHORT = 12345;
    public static final int INT = 123456789;
    public static final long LONG = 12345678901L;
    public static final float PI = 3.14f;
    public static final double E = 2.71828;
    
    public static void main(String[] args) throws RuntimeException {
        System.out.println("Hello, World!");
    }
}

方法信息就放在class文件的method_info结构中,方法的Java代码编译成字节码存method_info的Code属性的code子属性中

字节码中存放编码后的Java虚拟机指令。每条指令都以一个单字节的操作码(opcode)开头,这就是字节码名称的由来。由于只使用一字节表示操作码,显而易见,Java虚拟机最多只能支持256(2^8 )条指令。
到第八版为止,Java虚拟机规范已经定义了205条指令,操作码分别是0(0x00)到202(0xCA)、254(0xFE)和255(0xFF)。
这205条指令构成了Java虚拟机的指令集(instruction set)。

和汇编语言类似,为了便于记忆,Java虚拟机规范给每个操作码都指定了一个助记符(mnemonic)。比如操作码是0x00这条指令,因为它什么也不做,所以它的助记符是nop(no operation)。

Java虚拟机使用的是变长指令,操作码后面可以跟零字节或多字节的操作数(operand)。如果把指令想象成函数的话,操作数就是它的参数。为了让编码后的字节码更加紧凑,很多操作码本身就隐含了操作数,比如把常数0推入操作数栈的指令是iconst_0。

0xB2

可以看到,该指令的操作码是0xB2,助记符是getstatic。

它的操作数是0x0002,代表常量池里的第二个常量。


0x0002

在第4章中讨论过,操作数栈和局部变量表只存放数据的值,并不记录数据类型。
结果就是:指令必须知道自己在操作什么类型的数据。这一点也直接反映在了操作码的助记符上。
例如,
iadd指令就是对int值进行加法操作;
dstore指令把操作数栈顶的double值弹出,存储到局部变量表中;
areturn从方法中返回引用值。
也就是说,如果某类指令可以操作不同类型的变量,则助记符的第一个字母表示变量类型。

Java虚拟机规范把已经定义的205条指令按用途分成了11类:

  • 常量(constants)指令:
    把常量推入操作数栈顶。常量可以来自三个地方:隐含在操作码里、操作数运行时常量池。常量指令共有21条

加载(loads)指令、
存储(stores)指令、
操作数栈(stack)指令、
数学(math)指令、
转换(conversions)指令、
比较(comparisons)指令、
控制(control)指令、
引用(references)指令、
扩展(extended)指令
保留(reserved)指令。

保留指令一共有3条。其中一条是留给调试器的,用于实现断点,操作码是202(0xCA),助记符是breakpoint
另外两条留给Java虚拟机实现内部使用,操作码分别是254(0xFE)和266(0xFF),助记符是impdep1impdep2
这三条指令不允许出现在class文件中。

5.2 指令和指令解码

Java虚拟机规范的2.11节介绍了Java虚拟机解释器的大致逻

do {
  atomically calculate pc and fetch opcode at pc;
  if (operands) fetch operands;
  execute the action for the opcode;
} while (there is more to do);

每次循环都包含三个部分:
计算pc、指令解码、指令执行。
go伪代码

for {
  pc := calculatePC()
  opcode := bytecode[pc]
  inst := createInst(opcode)
  inst.fetchOperands(bytecode)
  inst.execute()
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 第6章类文件结构 6.1 概述 6.2 无关性基石 6.3 Class类文件的结构 java虚拟机不和包括java...
    kennethan阅读 980评论 0 2
  • Java 虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至...
    好好学习Sun阅读 585评论 0 0
  • 银东县城有位刘大妈,她可是位在方圆百里小有名气的接生婆。 刘大妈从二十几岁便开始为人接生,到五十多岁,经她接生的孩...
    无为山人阅读 511评论 0 0
  • 一 再几日 可夭夭 可灼灼 冠名的潭水 吟为美酒千盅 嫣红轻浅 寻作城南的优柔 我打马驰过 以笔作鞭 谁是我的汪伦...
    张纲阅读 208评论 0 1
  • 成了成年人,终于知道了成年人的焦虑。我妈发烧住院的几天,我要照顾妈妈又要照顾女朋友,感觉哪一点做不好就心里对不起她...
    A01琪公子阅读 216评论 0 0