引言
这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战
今天来聊聊java的字节码技术,java虚拟机的高效执行离不开字节码,字节码贴近机器指令但不是纯粹的字节码指令,读完本篇文章你会了解到字节码指令并能阅读一个简单的字节码指令程序,并洞悉字节码的作用。
Java的编译
我们的java源代码是通过我们的这个javac编译生成字节码,java虚拟机规范中把这一行为叫生成机器码的解释列表,然后,我们可以用javap进行查看生成的字节码,javap是Oracle用虚拟机语言开发的一款工具,虚拟机规范把这一行为叫生成编译方法的示例。
编码格式
<index> <opcode> [ <operand1> [ <operand2>... ]] [<comment>]
index
:中文名叫索引,可以看成是字节数组中的索引或者是方法开头的偏移量opcode
:操作码,这里是用来当作操作码的指令助记符的zero or more
`` :0或多个,代表指令的操作数comment
:注释语法run-time constant pool index
:with a#
,比如#1
栈指令操作
Java 虚拟机是面向堆栈的,大多数操作从 Java 虚拟机当前帧的操作数堆栈中获取一个或多个操作数,或者将结果推回到操作数堆栈上。Java 虚拟机提供了大量指令,可以将操作数堆栈的内容作为无类型值进行操作,这里挑一些介绍,如下:
aload
:将第一个引用类型局部变量推送至栈顶dup
:复制栈顶数值并将复制值压入栈顶dup_x1
复制栈顶数值并将两个复制值压入栈顶dup2_x1
dup_x1 指令的双倍版本
对象指令操作
方法调用指令操作
对于对象的操作,很常见的就是对方法的操作和调用,那虚拟机给我们提供的指令就是invokevirtual,下面也给出解释了,各位可以看下。
我们来看下该指令的实际位置:是在方法调用的位置上,可以看到还有常量池的引用,这个4这个操作数,它并不是类实例方法里的偏移量,它只是编译器生成的一个对该方法的实例引用,并将其储存到运行时常量池中,待常量池在运行时进行解析以获取到方法的实际位置。
类实例的创建指令操作
我们的java是操作对象执行程序的,对象从哪来呢?从我们的new
关键字,编译器看到new关键字,就会调用编译器命名的<init>
方法,这种特殊方法我们也叫类实例的初始化方法。
下面这个图我们可以看到方法调用,调用的什么方法呢?噢,类实例的初始化方法。
流程控制指令操作
循环指令操作
其实,循环大家不要把循环想复杂了,循环就是给定一个命令区域,让程序反复的在里面执行,直到条件满足就跳出区域,执行其它指令,这里以for
为例,带大家玩玩。
cmp是比较的意思,Lt是less than小于,如果比较的数小于100,指令就怎么样?调到索引5的位置继续执行。
数据运算指令操作
Java 虚拟机通常对其操作数堆栈进行算术运算,为此也提供了一些算术指令,我们用来计算的算术运算的操作数会从操作数堆栈中弹出,然后经过计算得到的结果会推回到操作数堆栈中,这样我们可以进行嵌套操作,例如:
int align2grain(int i, int grain) {
return ((i + grain-1) & ~(grain-1));
}