详解 JVM 字节码(3)

版本信息
上一次我们分析前 4 个字节为魔数,继续向下数 4 字节为版本号信息(前两个字节表示此版本号,后两个字节表示主版本号)
00 00 00 36 。36 是 16 进制进行换算 3 * 16 + 6 = 54 这里用的 java 的版本号 10.0.1 版本。
minor version: 0
major version: 54
常量池
接下来就是常量池,常量池大小是不固定的。
常量池定义与作用
Java 类中定义的很多信息都是由常量池来维护和描述的。而且字节码其他部分都会引用常量池的内容。所以将常量池看成 Java 类资源仓库。
主要存储两类常量
- 字面量:文本字符串,java 中声明为 final 的常量值
- 符号引用 :类和接口的全局(包名)限定名,字段和方法的名称和描述符
常量池的结构
- 常量池数量:占据 2 字节 00 22
- 常量池数组:常量池数组中不同元素的类型、结构都是不同的。每一种元素第一个数据都是 u1 类型,是标志位,占一个字节。JVM 在解析常量池时,会根据这个 u1 类型获取元素具体信息。
这里常量池数量 22 16 * 2 + 2 = 34
#1 = Methodref #6.#28 // java/lang/Object."<init>":()V
#2 = String #29 // basic
#3 = Fieldref #5.#30 // com/zidea/Tut.title:Ljava/lang/String;
#4 = Fieldref #5.#31 // com/zidea/Tut.courses:I
#5 = Class #32 // com/zidea/Tut
#6 = Class #33 // java/lang/Object
#7 = Utf8 title
#8 = Utf8 Ljava/lang/String;
#9 = Utf8 courses
#10 = Utf8 I
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lcom/zidea/Tut;
#18 = Utf8 getTitle
#19 = Utf8 ()Ljava/lang/String;
#20 = Utf8 setTitle
#21 = Utf8 (Ljava/lang/String;)V
#22 = Utf8 getCourses
#23 = Utf8 ()I
#24 = Utf8 setCourses
#25 = Utf8 (I)V
#26 = Utf8 SourceFile
#27 = Utf8 Tut.java
#28 = NameAndType #11:#12 // "<init>":()V
#29 = Utf8 basic
#30 = NameAndType #7:#8 // title:Ljava/lang/String;
#31 = NameAndType #9:#10 // courses:I
#32 = Utf8 com/zidea/Tut
#33 = Utf8 java/lang/Object
为什么是 33 而不是 34,常量池数组个数等于常量池数 - 1 ,其中 0 暂时不使用,索引为 0 是 JVM 保留常量,这个常量不位于常量池,这个常量对应 null。

这个表不用记,学会查表就行
tag - U1 表示常量类型
1. java/lang/Object."<init>":()V
0A: 10
对应查表 U1 为 10 是 CONSTANT_Methodref_Info
00 06 (6)index: 指向声明方法的类描述符CONSTANT_Class_Info的索引项
00 1C(对应十进制 28) index: 指向名称及类型描述符 CONSTANT_NameAndType_Info的索引项
#1 = Methodref #6.#28 // java/lang/Object."<init>":()V
-
#6 = Class #33 // java/lang/Object
-
#33 = Utf8 java/lang/Object
#28 = NameAndType #11:#12 // "<init>":()V -
#11 = Utf8 <init>
-
#12 = Utf8 ()V
这里出现许多看似简写的符号,看一看他们都代表什么含义?
在 JVM 规范中,每个变量/字段都有描述信息,用于描述字段的数据类型,方法的参数列表(数量、类型与顺序)与返回值。
描述规则基本数据类型和代表无返回值的 void 类型都用一个大写字符来表示,对象类型则使用字符L加对象全局名称来表示。 -
B : byte
-
C : char
-
D : double
-
F : float
-
I : Int
-
J : Long
-
S : short
-
Z: boolean
-
V: void
-
L : 对象 Ljava/lang/String
数组类型来说,每一个维度使用一个前置[来表示,例如int[] 被记录为[I,String[][] 被记录为[[Ljava/lang/String。
描述符描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序存放在一组()之内。
public void setTitle(String title)
编译为字节码(Ljava/lang/String;)V
2. basic
08 标识符对应 CONSTANT_String_Info
- 00 1D(29) index 指向字符串字面量索引
- #29 = Utf8 basic
在 Tut 类将 title 字段赋值为 basic 这个字符串。
3. com/zidea/Tut.title:Ljava/lang/String;
09 标识符对应 CONSTANT_Fieldref_Info
- 00 05 (5): 指向声明字段的类或者接口描述符 CONSTANT_Class_Info 的索引
- 00 1E (30): 指向字段描述符 CONSTANT_NameAndType_Info 的索引值
指向类描述符为 com/zidea/Tut 为字段所属的类
字段名称和类型 #30 = NameAndType #7:#8
#7: title 表示字段名称
#8: Ljava/lang/String 字段类型为字符串
表示 Tut 类的 String 类型变量 title
4. com/zidea/Tut.courses:I
09 标识符 CONSTANT_Fieldref_Info
00 05 (5)重复就不多解释了,代表类
00 1F (31)
31 = NameAndType #9:#10 // courses:I
- course 为变量
- I 为类型 int
以上就结束两个变量的声明
5. com/zidea/Tut
07 对应标识符为 CONSTANT_Class_Info
00 20 (32) 指向全局限定名常量项的索引
32 = Utf8 com/zidea/Tut
这个类全限定名称
6. java/lang/Object
又是 07 大家自己尝试分析
7. title
01 标识符为 CONSTANT_utf8_Info
- 00 05 length 表示五个字节,title 就是由五个字符组成的。
- 74 69 74 6C 65 分为 title 五个字符的ASCII码
8. Ljava/lang/String;
- 00 12 (18) 向后数 18 字节
- 4C 6A 61 .!...title...Lja
00000030: 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B(对应字符 Ljava/lang/String;)
9. courses
10. I
...
#33 = Utf8 java/lang/Object
我相信大家已经了回了怎么进行分析,大家可以自己完成。
这两号表示当前的字节码文件是由那个文件编译出来的。
26 = Utf8 SourceFile
27 = Utf8 Tut.java
在字节码使用/
代替.
分隔符。