涉及知识点:
- 符号引用和直接引用
- JVM指令集
编写一个类:HelloWorld.java
文件所在目录:/home/work/test/
public class HelloWorld {
private static int num = 0;
public String welcome = "HelloWorld";
public static void main(String[] args) {
String[] strArray = {"cainiao", "yuanyuan"};
for (int i = 0; i < 10; i++) {
HelloWorld.num++;
if(i == 5) {
continue;
}
System.out.println("HelloWorld!");
}
}
private long myMethod() {
return 99L;
}
}
注意,代码里不需要写包路径,因为我们等会儿是直接进入到HelloWorld.java所在位置执行编译操作,如果有包路径,需要再建立对应代码目录,徒增干扰。
编译类文件
进入/home/work/test/目录
执行:
javac HelloWorld.java
生成HelloWorld.class文件,这就是类HelloWorld.java的字节码文件。
使用工具打开HelloWorld.class字节码文件
idea推荐插件jclasslib bytecode viewer,官网安装插件地址:https://plugins.jetbrains.com/plugin/9248-jclasslib-bytecode-viewer
使用该插件打开字节码,界面如下:
查看翻译后的字节码和16进制字节码对应,可使用classpy工具。
使用classpy打开HelloWorld.class,界面如下:
左侧为字节码内容解析后的可视化结构,右侧为对应的16进制字节码。
Java官方虚拟机规范中,对类的class文件格式说明文档:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html 我们所有的讲解都将以官方文档为准。
class字节码文件整体结构:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
上述整体结构的描述格式仅仅是表示出class文件中有哪些信息,以及每项信息的长度或类型,并不是class文件真正的格式,不要误会!后文类似结构描述均是如此。
其中的u2、u4:
在计算机中,8bit=1Byte(8位等于1个字节)。类文件中的各项信息长度不同,使用u2、u4表示该项信息的长度,u表示无符号整数。
- magic:信息长度为4个字节,则使用u4表示
- minor_version:信息长度为2个字节,则使用u2表示
其中的cp_info、field_info、method_info、attribute_info:
- cp_info:常量池中所有常量的列表
- field_info:类中的具体变量表(指代码中的num、welcome,不包括方法中的变量strArray)
- method_info:类中的方法表
- attribute_info:类中的属性表(类本身的属性信息,不是类中的字段,类中的字段在field_info项表示)
HelloWord.class中对应上述类文件格式的各项信息如下:
简要介绍class文件各部分内容
先对class文件中的各个部分进行简要介绍,然后对每个部分详细介绍,所以这一段看不懂没关系,先大致了解。
- magic:魔数,是class文件的标识符
- minor_version, major_version:表示编译该class文件使用的JDK版本号
- constant_pool_count:该类的常量池中常量数量
- constant_pool[]:该类常量池中的常量明细表
- access_flags:该类的访问权限标识符,如public、private、abstract
- this_class:当前类自身是谁,保存的是当前类的名字在常量池中对应常量的索引值(对应上述示例,this_class指向常量池中的一个字符串常量:HelloWorld)
- super_class:表示当前类的父类是谁,取值只能为0或父类的名字在常量池中对应常量的索引值(对应上述示例,super_class指向常量池中的一个字符串常量:java/lang/Object。当本类即为Object类时,super_class取值为0)
- interfaces_count:表示当前类直接实现的接口数量
- interfaces[]:表示当前类直接实现的接口明细表
- fields_count:表示当前类中字段的数量
- fields[]:表示当前类中字段明细表(对应上述示例中的变量:num、welcome)
- methods_count:表示当前类中方法的数量
- methods[]:表示当前类中方法明细表
- attributes_count:表示当前类的属性数量
- attributes[]:表示当前类的属性明细表(要区别属性和字段,类的属性如当前class文件对应的源代码文件名:HelloWorld.java)
详细介绍class文件各部分内容
下面对类文件结构的各部分逐一解释。
magic:魔数
是class文件的标识符,长度:4字节。
当文件的前4个字节为0xCAFEBABY时,JVM才会认为当前文件为java类的字节码文件。
尝试使用文本编辑器修改HelloWorld.class文件,如修改为“CAFE BABA”:
执行:
java HelloWorld
JVM报错:魔数错误。(错误信息中的1667327589,是16进制0xCAFEBABE的10进制数值)
minor_version,major_version:次版本号(小版本号),主版本号(大版本号)
二者共同组成一个版本号,表示编译出当前字节码文件所使用的JDK版本号。
minor_version=0x0000=0,major_version=0x0034=52,则编译出当前字节码文件所使用的JDK版本号是:52.0。
再根据以下JDK版本与class文件版本号对应关系(第1列是JDK版本,第2列是class文件版本号),可知编译使用的JDK版本是:8(即JDK8)。
第3列表示JDK版本对应可处理的class文件版本号区间,如JDK8支持处理版本号在[45~52]之间的class文件。
官方文档中说明:
JDK1.0.2版本,支持45.0~45.3版本的class文件
JDK1.1.*版本,支持45.0~45.65535版本的class文件(即16进制版本号区间:0x0000~0xffff)
JDK1.2及以上版本,class文件版本号支持情况如上表所示。
constant_pool_count:常量池中常量的数量
常量池中常量数量=(下面)常量表中常量数-1
constant_pool:常量池中的常量表
常量池表是class文件中的重要组成部分。说到java中的常量池,其实有两部分:
- 静态常量池:就是class文件中写到的常量池;
- 运行时常量池:class文件被加载到JVM内存之后,class文件中的常量池保存在了方法区。通常说道“常量池”都是指运行时常量池。
常量池表中描述每个常量信息的格式:
cp_info {
u1 tag;
u1 info[];
}
tag:常量池中描述每个常量时,都需要一个1字节长度的tag字段,表示该常量的类型。JVM规范中官方给出的常量类型和对应的数值如下表所示:
Constant Type(常量类型) | Value | 解释 |
---|---|---|
CONSTANT_Class |
7 | 类或接口【本身】(完全限定名)的常量 |
CONSTANT_Fieldref |
9 | 类或接口中【字段】的符号引用 |
CONSTANT_Methodref |
10 | 类或接口中【普通方法】(非接口)的符号引用 |
CONSTANT_InterfaceMethodref |
11 | 接口中接口【方法】的符号引用 |
CONSTANT_String |
8 | 表示java.lang.String类型字符串常量 |
CONSTANT_Integer |
3 | 表示4字节int型数值常量(实际数值) |
CONSTANT_Float |
4 | 表示4字节float型数值常量(实际数值) |
CONSTANT_Long |
5 | 表示8字节long型数值常量(实际数值) |
CONSTANT_Double |
6 | 表示8字节double型数值常量(实际数值) |
CONSTANT_NameAndType |
12 | 表示字段或方法的【类型+名称】符号引用 |
CONSTANT_Utf8 |
1 | 字符串常量的值!(实际字符串的值) |
CONSTANT_MethodHandle |
15 | 表示方法句柄 |
CONSTANT_MethodType |
16 | 表示方法类型 |
CONSTANT_InvokeDynamic |