类加载器Classloader、运行时数据区域(JVM管理的内存)、执行引擎(即时编译器、解释器、垃圾回收器等)、本地接口(本地已经编译的方法比如虚拟机中提供的c/c++方法)。
接下来要学的是类加载器、运行时数据区域、垃圾回收器、即时编译器。解释器和本地接口是JVM的底层源码部分,程序员无法进行修改,如果有兴趣再去了解。
在学这些之前,先了解一下输入JVM的字节码文件是怎么回事。
(1)如何以正确的姿势打开字节码文件?
字节码文件是以二进制进行存储的,win上的记事本、notepad++等软件都无法打开查看。
可以使用jclasslib
这个软件来查看字节码文件。
(2)字节码文件是如何组成的?
一个字节码文件由以下部分组成:
第一部分:基本信息
- 魔数:一个文件无法通过文件扩展名确定文件类型。 文件扩展名可以随意修改,不会影响文件的内容。软件是通过文件的头几个字节去校验文件的类型,如果软件不支持该种类型就会出错。Java字节码文件中,将文件头(CAFRBABE)称为magic魔数。
- 主副版本号:编译字节码文件的jdk版本号。主版本号用来标识大版本号,副版本号用来标识小版本号。jdk1.2之后大版本号计算方法是:主版本号-44。比如主版本号52就是jdk8,主版本号61就是jdk17。一般只关心主版本号。版本号的作用是用来判断jdk版本是否与当前字节码文件兼容,如果不兼容就不能用当前jdk解释该字节码文件。版本号不兼容,升级jdk或者将第三方依赖的版本号降低或者更换依赖。一般来说都是采用第二种,因为影响比较小
第二部分:常量池
其主要保存的就是常量。每一个常量都有一个编号(编号从1开始)。字节码文件中所有的字符串都能在这里找到。常量池里边其实也是有分类型的:字符串对象(保存着指向字符串常量的编号)、方法名、类名、字符串常量。
在字段和方法的字节码指令中,通过常量编号可以快速找到对应的常量。这种方式称为符号引用。
第三部分:字段
其实就是类的属性。字段名会指向常量池中的字符串。
第四部分:方法
方法区域是存放字节码指令的核心位置,字节码指令的内容存放在方法的code属性之中。
面试中会遇到一道比较经典的题:
int i=0;i=i++
结果是多少或者int i=0;i=++i
结果是多少。这时候就可以从字节码指令来解释了。
(3)字节码有哪些常见工具?
javap -v 字节码文件名称
可以查看字节码文件内容。如果是jar包,需要先用jar -xvf
进行解压。java -jar arthas-boot.jar
运行,选择想要监控的Java进程,然后看文档找命令即可。查看字节码、反编译等等它都行。