前言
我们知道JVM被称为虚拟机,它必然要有一套程序指令集,这样CPU才能和你的程序做交互,所以Java字节码指令基本可以对应到CPU的指令。
对于JVM来说,程序的执行过程就是入栈出栈的过程。在这个过程中伴随着从局部变量表存值取值,然后压栈,然后对栈内数据做计算(出栈)。此外,可以通过程序流程控制指令(改变程序计数器),达到循环、分支、跳转的效果。如果程序需要改变对象(堆),需要用到对象操作指令。如果变量类型是兼容的,编译器还会加入转换指令(基本类型)。
按照指令的性质可以把指令分为以下几类:
- 操作指令
- 栈操作指令
- 局部变量表操作指令
- 程序流程控制指令
- 对象操作指令
- 算术运算指令
- 类型转换指令
- 调试操作指令
操作指令
栈操作指令
- 局部变量表前入栈
- iconst_1,表示把常数1压栈
- 前面部分包含:i、l、f、d,分别表示int、long、fload、double(todo:对于boolean、char、byte、short是怎么处理的)。a表示references类型。
- 当int值为-1~5时:iconst_1类似表示;用iconst_m1表示-1
- 当int值为-128~127时:bipush 126表示
- 当int值为-32768~32767时:sipush 32765表示
- 当int值为-2147483648~2147483647时:ldc 2147483647表示
- dup:压栈对象(reference类型)
- 出栈
- 当执行算术运算指令时,后面跟的参数从栈出
局部变量表操作指令
- store:从栈中弹出值并存到局部变量表中
- load:从局部变量表取出数据并加载到栈中
- 指令前参数表示类型,指令后数字表示局部变量表槽位置:aload_1,表示从位置1取出类型为references的值并加载到栈中
程序流程控制指令
- if_icmpge
- if => if
- icmp => int compare
- ge => 前面load的两个值比较
例子:todo
对象操作指令
- new
- Invokestatic:顾名思义,这个指令用于调用某个类的静态方法,这是方法调用指令中最快 的一个。
- Invokespecial :用来调用构造函数,但也可以用于调用同一个类中的 private 方法, 以及可 见的超类方法。
- invokevirtual :如果是具体类型的目标对象,invokevirtual 用于调用公共、受保护和 package 级的私有方法。
- invokeinterface :当通过接口引用来调用方法时,将会编译为 invokeinterface 指令。
- invokedynamic : JDK7 新增加的指令,是实现“动态类型语言”(Dynamically Typed Language)支持而进行的升级改进,同时也是 JDK8 以后支持 lambda 表达式的实现基础。
例子:todo
算术运算指令(执行过程会按需要的参数从操作数栈弹出数据,计算完成后压栈)
- add+
- sub-
- mul*
- div/
- rem%
- neg-()
类型转换指令
- 2: i2l
调试操作指令
示例及工具使用
示例1
java代码
package cc.page.jvm.study.binarycode;
public class HelloByteCode {
public static void main(String[] args) {
HelloByteCode obj = new HelloByteCode();
}
}
javap查看字节码,【】内是我对代码的解释
javap -c HelloByteCode.class
public class cc.page.jvm.study.binarycode.HelloByteCode {
public cc.page.jvm.study.binarycode.HelloByteCode();
Code:
0: aload_0 【从局部变量表位置0取出值,this】
1: invokespecial #1 // Method java/lang/Object."<init>":()V【todo调用常量池中位置1对应的方法,()V表示返回值为void】
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class cc/page/jvm/study/binarycode/HelloByteCode【new一个对象,参数指向常量池#2也就是HelloByteCode类地址】
3: dup【将上面的对象压栈】
4: invokespecial #3 // Method "<init>":()V【调用对象方法init,返回值为void】
7: astore_1【从栈中弹出弹出值并保存到局部变量表位置1处】
8: return
}
注:字节码前面的数字代表在真正的字节码文件中的偏移位置,一个位置一个字节
打印局部变量表和常量池等信息
注意,常量池#1指向的应该就是常量池的标识符,值应该是存在运行时常量池中,所以应该还要去常量池
javap -c -verbose HelloByteCode.class
Classfile /Users/xuqingbin/Documents/myself/架构师进阶/workspace/jvm/target/classes/cc/page/jvm/study/binarycode/HelloByteCode.class
Last modified 2021-11-11; size 473 bytes
MD5 checksum 90b484786b1f7f825804e420dd281f31
Compiled from "HelloByteCode.java"
public class cc.page.jvm.study.binarycode.HelloByteCode
minor version: 0
major version: 52【Java8】
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#19 // java/lang/Object."<init>":()V
#2 = Class #20 // cc/page/jvm/study/binarycode/HelloByteCode
#3 = Methodref #2.#19 // cc/page/jvm/study/binarycode/HelloByteCode."<init>":()V
#4 = Class #21 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 LocalVariableTable
#10 = Utf8 this
#11 = Utf8 Lcc/page/jvm/study/binarycode/HelloByteCode;
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 args
#15 = Utf8 [Ljava/lang/String;
#16 = Utf8 obj
#17 = Utf8 SourceFile
#18 = Utf8 HelloByteCode.java
#19 = NameAndType #5:#6 // "<init>":()V
#20 = Utf8 cc/page/jvm/study/binarycode/HelloByteCode
#21 = Utf8 java/lang/Object
{
public cc.page.jvm.study.binarycode.HelloByteCode();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:【代码与字节码偏移开始位置对应关系】
line 3: 0
LocalVariableTable:【局部变量表】
Start Length Slot Name Signature
0 5 0 this Lcc/page/jvm/study/binarycode/HelloByteCode;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class cc/page/jvm/study/binarycode/HelloByteCode
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: return
LineNumberTable:
line 6: 0
line 7: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
8 1 1 obj Lcc/page/jvm/study/binarycode/HelloByteCode;
}
SourceFile: "HelloByteCode.java"
网友评论