专栏导航
目录
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了字节码文件的基础信息、常量池、方法、字段、属性等内容。
字节码文件的基础信息包括魔数、字节码文件对应的Java版本号、访问标识(public final等等)、父类和接口内容。
文件是无法通过文件扩展名来确定文件类型的,文件扩展名可以随意修改,不影响文件的内容。软件使用文件的头几个字节(文件头)去校验文件的类型,如果软件不支持该种类型就会出错。在Java字节码文件中,将文件头称为magic魔数。
文件类型 | 字节数 | 文件头 |
JPEG (jpg) | 3 | FFD8FF |
PNG (png) | 4 | 89504E47(文件尾也有要求) |
bmp | 2 | 424D |
XML (xml) | 5 | 3C3F786D6C |
AVI (avi) | 4 | 41564920 |
Java字节码文件 (.class) | 4 | CAFEBABE |
通过NotePad++使用十六进制插件查看class文件:
版本号用于标识字节码文件的版本。它包括主版本号和副版本号。主版本号表示编译字节码文件的JDK版本,而副版本号则用于标识不同版本的字节码文件。通过比较字节码文件的主版本号和运行时的JDK版本,可以判断两者是否兼容。
JDK1.0-1.1使用了 45.0-45.3,JDK1.2是46之后每升级一个大版本就加1。1.2之后大版本号计算方法为主版本号 - 44,比如主版本号52就是JDK8。
访问标识:在Java字节码文件中用于描述类的访问权限和特性。它包括public、private、protected和默认(无标识)等访问级别,以及final、abstract、interface等修饰符。这些标识确定了类的可见性和行为特性,影响着类的使用和继承。
类、父类、接口索引:在字节码文件中,父类和接口的内容通过索引值来表示。这些索引值指向类或接口在常量池中的位置,以便在运行时能够找到它们的相关信息。通过这些索引值,JVM可以在运行时加载并链接所需的类和接口,从而正确执行程序。
常量池是字节码文件中的一个重要组成部分,主要用于存储程序中的常量值,如字符串常量、类或接口名、字段名等。常量池的主要作用是避免相同的内容在字节码文件中重复定义,从而节省空间。
在字节码指令中,可以通过常量池中的编号引用相应的常量值。每个常量在常量池中都有一个唯一的编号,编号从1开始。通过这些编号,字节码指令可以快速地定位到常量池中的数据,从而实现高效的数据访问。在字节码指令中引用常量池的过程称为符号引用。通过符号引用来替代实际的数据值,可以在运行时动态地解析和加载相应的数据,提高程序的灵活性和可维护性。
案例一:
案例代码:
public class ConstantPoolTest {
public static final String a1 = "This is a test";
public static final String a2 = "This is a test";
public static void main(String[] args) {
ConstantPoolTest constantPoolTest = new ConstantPoolTest();
}
}
查看字段常量值索引(a1和a2),指向cp_info #8:
进入常量池cp info #8,看到字符串文本内容在cp_info #27:
?进入常量池cp_info #27,可以看到字符串文本内容:
在上面的示例中,字符串常量"This is a test"在常量池中只存储一次。在字节码指令中,可以通过常量池中的编号来引用这两个常量。因此,常量池避免了相同内容的重复定义,节省了存储空间。
案例二:
案例代码:
public class ConstantPoolTest2 {
public static final String a1 = "abc";
public static final String a2 = "abc";
public static final String abc = "abc";
public static void main(String[] args) {
ConstantPoolTest2 constantPoolTest = new ConstantPoolTest2();
}
}
查看字段常量值索引,指向cp_info #8:
?进入常量池cp info #8,看到字符串文本内容在cp_info #10:
?进入常量池cp_info #10,可以看到字符串文本内容:
查看字段abc名称所在常量池,指向cp_info #10:
在JVM字节码文件中,方法部分是核心,它包含了程序执行的具体指令。这些指令是以字节码的形式存在的,是Java源代码的编译结果。
右键查看JVM规范:
?JVM规范:
案例一:
案例代码:
public class Demo1 {
public static void main(String[] args) {
int i=0;
i = i++;
}
}
?查看方法的字节码信息:
?字节码信息解析:
iconst_0 | 将0放入操作数栈 |
istore_1 | 从操作数栈取出放入局部变量表1号位置 |
iload_1 | 从局部变量表1号位置加载数据到操作数栈 |
iinc 1 by 1 | 在局部变量表1号位置增加1 |
istore_1 | 将操作数栈中的值保存到局部变量表 |
return | 方法结束,返回 |
案例二:
案例代码:
public class Demo2 {
public static void main(String[] args) {
int i = 0;
int j = i + 1;
}
}
?查看方法的字节码信息:
??字节码信息解析:
iconst_0 | 将常量0放入操作数栈 |
istore_1 | 从操作数栈取出放入局部变量表1号位置 |
iload_1 | 将局部变量表1中的数据放入操作数栈 |
iconst_1 | 将常量1放入操作数栈 |
iadd | 将操作数栈顶部的两个数据进行累加,结果放入栈中 |
istore_2 | 从操作数栈取出放入局部变量表2号位置 |
return | 方法结束,返回 |
在JVM字节码文件中,字段部分包含了当前类或接口声明的字段信息。这些字段包括类的成员变量、静态变量、常量等。字段信息在类的字节码文件中被组织为一个字段表,每个字段在表中都有一个唯一的字段表条目。
在JVM字节码文件中,属性部分包含了描述类或接口的各种元数据信息。这些属性提供了关于类或接口的附加信息,用于支持某些JVM特性和工具。
常见的属性:
JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了字节码文件的基础信息、常量池、方法、字段、属性等内容,希望对大家有所帮助。