字节码文件解剖
.java 文件通过 java -c 生成.class 文件,这部分并非是 JVM 需要处理的部分,JVM 处理的部分是基于生成的 class 文件,生成的部分是由编译器来负责
一个字节码文件的主要组成部分
使用工具说明
idea 的 JclassLib 插件
使用步骤:
1、运行代码(只要你更新了代码就需要,或者 build)
基础信息介绍
魔数
魔数并非在这里展示,魔数其实有点类似一个文件的格式中的一部分,所有的软件开发中关于打开文件都会这么设计,用于识别说我们能否成功打开
java 中是开头是 cafebabe
其他文件如下:
主版本号
1.2 后大版本对应的公式是主版本号-44
关于版本号可能遇到的 problem 及解法
版本不匹配提高 java 版本降低依赖版本
在这之中可能还会遇到的问题:jdk 版本存在 bug,虽然很多人都说什么永远 jdk8,但是实际使用上 jdk8 在性能上以及开发上可能会比 jdk17 多不少工作量,例如我之前尝试转为 http2,而在 jdk8 需要引入其他依赖,而 jdk9 则可以直接转
常量池介绍以及属性介绍
此常量池非 JVM 中的字符串常量池,仅仅是当前 class 文件中为了降低一定的空间占用和加快 JVM 解析 class 文件而使用的常量池而已
下列讨论也仅局限于字节码文件中的常量池,不会涉及 JVM 部分
在此先给常量池定论:为了节省空间而使用的池化技术
首先从字段入手:
Problem1:为什么 aaa 变量可以打开?而其他的不行?
解答:是因为他使用的是 static 和 final 同时修饰
所有以 cp 开头的都最终指向了常量池中的一个记录,例如 aaa 这个变量名,以及对应的 aaa 的类型 string,具体内容如下:
我们再看看 ConstantValue 的信息:
problem2:这个属性名索引为何存在且为什么要是 constantValue?
首先回答一下为什么要有 constantvalue,这是由于我们使用 final 修饰了,所以最终的效果就是有了一个指向 constantValue 的引用,也就是我们 java,字节码层面实现我们 final 修饰不可改变的效果,如下图所示,而这个属性值为什么存在也不言而喻了
最后回到正题:我们可以发现当 String aaa 和 Stirng ccc 都="aaa"时,他的常量值索引都指向 7
那么 7 的内容是什么?7 的类型是 string_info,而之中又指向了 8?
8 才是真正的 aaa
那么问题来了?
为什么要这么设计?
关于字段中的 constant_value 为什么要指向 string?然后 string 再指向其中的一个具体的 utf8_info 呢?
因为最终 jvm 会有一个 string 常量池,我们要保存 string 和 utf8_info 中保存的值的关系,好方便之后我们将其存储到常量池中
追问:那为什么不直接保存在对应的 string 中,也即我们 string 不存符号引用而是直接存对应的常量?
这样的话如果有其他变量也纸箱这里应该也没问题吧?
没错,但是例如 string abc="abc",对应的变量名也要存储,而 java 中采用的变量名的引用就指向对应的 utf_8 的"abc"
那么 abc 直接指向这个 string 不行吗?
可以,不过这样的话,能够更节省一些空间复杂度,但是会消耗更多的时间,尤其对于变量之后运行中的处理
总结
常量池是把对应的我们会使用到的常量抽出来,无论是各个参数或者变量名类名等等,他的核心之一就是要复用
而字段在目前阶段只会对于 final static 修饰的,因为目前能够处理的也只有些 final 和 static 修饰的值,其他是不行的,也就是编译阶段能处理的也就这一些了
其他字段的赋值需要之后类的生命周期在各个阶段再进行赋值
方法介绍
其中字节码部分就是对应方法的执行流程
而异常表主要是 trycatch 才会有的
而杂项对应的操作数栈的深度是字节码运行之中对于操作数栈使用的时候会使用到的栈的深度(与数据结构无异,而最大深度是因为在这之中便可以计算出来)
局部变量最大槽数是这里:也就是方法中使用的局部变量,包括参数等等
关于属性就不赘述了,意义不大
文章转载自:海山了
评论