JVM虚拟机—字节码
工具
-
javap
javap 是 JDK 自带的反解析工具。它的作用是将 .class 字节码文件解析成可读的文件格式。javac中可以指定一些额外内容输出到字节码。
- javac -g:lines 强制生成 LineNumberTable。
- javac -g:vars 强制生成 LocalVariableTable。
- javac -g 生成所有的 debug 信息。
-
jclasslib
jclasslib 是一个图形化的工具,能够更加直观的查看字节码中的内容。它还分门别类的对类中的各个部分进行了整理,非常的人性化。同时,它还提供了 Idea 的插件,jclasslib 的下载地址:https://github.com/ingokegel/jclasslib
类加载和对象创建的时机
对象创建的几种方式
-
new 类名() 创建
-
使用 Class 的 newInstance 方法。
-
使用 Constructor 类的 newInstance 方法。
-
反序列化。
-
使用 Object 的 clone 方法。
当虚拟机遇到一条 new 指令时,首先会检查这个指令的参数能否在常量池中定位一个符号引用。然后检查这个符号引用的类字节码是否加载、解析和初始化。如果没有,将执行对应的类加载过程。
查看字节码
特殊字符含义
B 基本类型 byte
C 基本类型 char
D 基本类型 double
F 基本类型 float
I 基本类型 int
J 基本类型 long
S 基本类型 short
Z 基本类型 boolean
V 特殊类型 void
L 对象类型,以分号结尾,如 Ljava/lang/Object;
[Ljava/lang/String; 数组类型,每一位使用一个前置的"["字符来描述
test函数执行过程
public long test(long);
descriptor: (J)J
flags: ACC_PUBLIC
Code:
stack=4, locals=5, args_size=2
0: aload_0
1: getfield #2 // Field a:I
4: i2l
5: lload_1
6: ladd
7: getstatic #3 // Field C:J
10: ladd
11: lstore_3
12: lload_3
13: lreturn
LineNumberTable:
line 13: 0
line 14: 12
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 this LB;
0 14 1 num J
12 2 3 ret J
我们介绍一下比较重要的 3 三个数值。
<1>
首先,注意 stack 字样,它此时的数值为 4,表明了 test 方法的最大操作数栈深度为 4。JVM 运行时,会根据这个数值,来分配栈帧中操作栈的深度。
<2>
相对应的,locals 变量存储了局部变量的存储空间。它的单位是 Slot(槽),可以被重用。其中存放的内容,包括:
- this
- 方法参数
- 异常处理器的参数
- 方法体中定义的局部变量
<3>
args_size 就比较好理解。它指的是方法的参数个数,因为每个方法都有一个隐藏参数 this,所以这里的数字是 2。
字节码执行过程
详细的字节码指令:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html
(1)0: aload_0
把第 1 个引用型局部变量推到操作数栈,这里的意思是把 this 装载到了操作数栈中。
对于 static 方法,aload_0 表示对方法的第一个参数的操作。
(2)1: getfield #2
将栈顶的指定的对象的第 2 个实例域(Field)的值,压入栈顶。#2 就是指的我们的成员变量 a。
复制
#2 = Fieldref #6.#27 // B.a:I
…
#6 = Class #29 // B
#27 = NameAndType #8:#9 // a:I
(3)i2l
将栈顶 int 类型的数据转化为 long 类型,这里就涉及我们的隐式类型转换了。图中的信息没有变动,不再详解介绍。
(4)lload_1
将第一个局部变量入栈。也就是我们的参数 num。这里的 l 表示 long,同样用于局部变量装载。你会看到这个位置的局部变量,一开始就已经有值了。
(5)ladd
把栈顶两个 long 型数值出栈后相加,并将结果入栈。
(6)getstatic #3
根据偏移获取静态属性的值,并把这个值 push 到操作数栈上。
(7)ladd
再次执行 ladd。
(8)lstore_3
把栈顶 long 型数值存入第 4 个局部变量。
还记得我们上面的图么?slot 为 4,索引为 3 的就是 ret 变量。
(9)lload_3
正好与上面相反。上面是变量存入,我们现在要做的,就是把这个变量 ret,压入虚拟机栈中。
(10)lreturn
从当前方法返回 long。