JVM 实践 -- 实例解析字节码常量池
目标
本文通过一个具体案例将 class 文件常量池数据结构和二进制文件存储格式对应起来分析,帮助加深对字节码常量池的理解。
字节码存储格式
为了能看懂二进制文件,需了解 class 字节码的存储格式,如下图所示:
注:u4 代表无符号 4 字节。
具体说明如下:
本文只分析常量池部分。常量池的结构如下:
常量池是一个变长结构,它的第一部分是 2 字节表示的池大小;第二部分是常量池项集合(索引号从 1 开始,0 属于保留索引,供特殊情况使用。最多包涵 n-1 个元素,因为 long 和 double 类型的常量会占用 2 个索引位置)。
每个常量池项的结构用伪代码表示如下:
第一个字节表示类型(tag),然后接下来的几个字节表示具体的内容。
常量池类型总共有 14 种,如下图:
案例分析
下面开始分析实例,代码如下:
1)使用 javac 编译源文件:
javac Hello.java
2)使用 javap 工具查看.class:
javap -v Hello
可以看到常量池中总共有 38 个常量项(池大小=38+1=39)。同时 Long 占了 #13-#14 两个索引值。
3)使用 vi 查看.class 文件:
vi -b Hello.class
输入::%!xxd
十六进制表示结果如图:
4)将二进制里面的各个常量项逐个找出来,根据索引跟 javap 的输出结果做对应
下面是前面 7 个常量项的对应示意图,通过不同的颜色做区分:
分析如下:
1、前面 8 字节为魔数和版本号,第 9~10 字节为常量池大小为:0027,即:39。
2、索引 #1 的常量项类型为 0a,查表可知为 MethodRef 类型,该类型结构如下:
从图中可知,其 class_index 为 0002(#2),name_and_type_index 为 0003(#3)。
3、索引 #2 的常量项类型为 07,查表为 Class 类型,该类型结构如下:
其 name_index 为 0004(#4)
4、索引 #3 的常量项类型为 0c,查表为 NameAndType 类型,该类型结构如下:
其 name_index 为 0005(#5),descriptor_index 为 0006(#6)。
5、索引 #4~#6 常量项类型都为 01,查表为 Utf8 类型,该类型结构如下:
对应图中的黄、红、绿三条横线。
6、索引 #7 常量项类型为 09,查表为 Fieldref 类型,该类型结构如下:
其 class_index 为 0008(#8),name_and_type_index 为 0009(#9)。
剩下的常量项按上述分析方法可逐一找出,不再赘述。
参考资料:
1、《深入理解 JVM 字节码》-张亚
也欢迎关注我的公众号(搜索:Make IT Simple),一起学习交流。
▲ 欢迎关注“Make IT Simple”,一起搞定底层原理
版权声明: 本文为 InfoQ 作者【林昱榕】的原创文章。
原文链接:【http://xie.infoq.cn/article/ab3958e173c3fdcda3447d0e7】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论