现在的位置: 首页 > 综合 > 正文

JVM字节码初探

2018年02月03日 ⁄ 综合 ⁄ 共 1760字 ⁄ 字号 评论关闭

字节码格式

字节码是JVM的机器语言。JVM加载类文件时,对类中的每个方法,它都会得到一个字节码流。这些字节码流保存在JVM的方法区中。在程序运行过程中,当一个方法被调用时,它的字节码流就会被执行。根据特定JVM设计者的选择,它们可以通过解释的方式,即时编译(Just-in-time compilation)的方式或其他技术的方式被执行。

方法的字节码流就是JVM的指令(instruction)序列。每条指令包含一个单字节的操作码(opcode)和0个或多个操作数(operand)。操作码指明要执行的操作。如果JVM在执行操作前,需要更多的信息,这些信息会以0个或多个操作数的方式,紧跟在操作码的后面。

每种类型的操作码都有一个助记符(mnemonic)。类似典型的汇编语言风格,Java字节码流可以用它们的助记符和紧跟在后面的操作数来表示。

字节码指令集被设计的很紧凑。除了处理跳表的2条指令以外,所有的指令都以字节边界对齐。操作码的总数很少,一个字节就能搞定。这最小化了JVM加载前,通过网络传输的类文件的大小;也使得JVM可以维持很小的实现。

JVM中,所有的计算都是围绕栈(stack)而展开的。因为JVM没有存储任意数值的寄存器(register),所有的操作数在计算开始之前,都必须先压入栈中。因此,字节码指令主要是用来操作栈的。例如,在上面的字节码序列中,通过iload_0先把本地变量(local variable)入栈,然后用iconst_2把数字2入栈的方式,来计算本地变量乘以2。两个整数都入栈之后,imul指令有效的从栈中弹出它们,然后做乘法,最后把运算结果压入栈中。istore_0指令把结果从栈顶弹出,保存回本地变量。JVM被设计成基于栈,而不是寄存器的机器,这使得它在如80486寄存器架构不佳的处理器上,也能被高效的实现。

原始类型(primitive types)

JVM支持7种原始数据类型。Java程序员可以声明和使用这些数据类型的变量,而Java字节码,处理这些数据类型。下表列出了这7种原始数据类型:

类型
定义
byte 单字节有符号二进制补码整数
short 2字节有符号二进制补码整数
int 4字节有符号二进制补码整数
long 8字节有符号二进制补码整数
float 4字节IEEE 754单精度浮点数
double 8字节IEEE 754双精度浮点数
char 2字节无符号Unicode字符

原始数据类型以操作数的方式出现在字节码流中。所有长度超过1字节的原始类型,都以大端(big-endian)的方式保存在字节码流中,这意味着高位字节出现在低位字节之前。例如,为了把常量值256(0x0100)压入栈中,你可以用sipush操作码,后跟一个短操作数。短操作数会以“01 00”的方式出现在字节码流中,因为JVM是大端的。如果JVM是小端(little-endian)的,短操作数将会是“00 01”。

字节码基本准则

1.字长是根据JVM不同而定的,一般(并非一定)在32位机上是4个字节,64位机上是8个字节(使用8个字节很可能会潜在地存在内存浪费的情况),JVM规范上要求1个字必须至少能容纳integer型的值(4字节),2个字必须至少能容纳long型的值(8个字节)。JVM有不少定义会以字为单位,譬如reference(引用)、本地变量和栈
2.JVM操作由操作码和操作数组成,操作码是1字节的,因此最多只有256个操作码,操作数从0-n个字节不等(0表示没有操作数,一般是指令参数通过操作栈来获取,n不定,譬如像TABLESWITCH和LOOKUPSWITCH指令),即指令=操作码+操作数
3.每个操作如果需要从操作栈中读参数,则总是将这些参数出栈,如果操作有结果结果,总是会将结果入栈,后面可能会重复提到一点,如果没有提到,这是一个参考准则
4本地变量是以字为单位(如上,32位机一般是4个字节,也有一些64位的JVM字长是8个字节)为单位的,即使值是byte或short,对于long、double型的数据,在本地变量区中会占用2个位置(slot)
5.操作栈是以字为单位(如上,32位机一般是4个字节) ,即使值是byte或short,而对于long、double型的数据,在操作栈中会占用2个位置(slot)

抱歉!评论已关闭.