本文假定读者对Java Class文件格式有一些基本的了解,建议结合相关书籍进行对照阅读。java
package chapter6;
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}
复制代码
使用JDK1.8编译成class文件,而后经过WinHex打开工具
类型:u4
字节地址:00000000~00000003
值:0xCAFEBABEthis
类型:u2
字节地址:00000004~00000005
值:0x0000编码
类型:u2
字节地址:00000006~00000007
值:0x0034spa
将0x0034转换为十进制,计算获得52,对应版本号为JDK 1.8。3d
类型:u2
字节地址:00000008~00000009
值:0x0016code
将0x0016转换为十进制,计算获得22。因为容量计数是从1开始(若是没有特殊状况,一般都是从0开始),所以常量池中有21项常量,索引值范围为1~21。cdn
常量池中每一项常量都是一个表,表开始的第一位是一个u1类型的标志位(tag)。blog
tag类型:u1
tag字节地址:0000000A
tag值:0x07继承
查表可知这个常量属于CONSTANT_Class_info结构,表明一个类或者接口的符号引用。
name_index类型:u2
name_index字节地址:0000000B~0000000C
name_index值:0x0002
0x0002指向了常量池中的第2项常量。
tag类型:u1
tag字节地址:0000000D
tag值:0x01
查表可知这个常量属于CONSTANT_Utf8_info结构,表明一个UTF-8编码的字符串。
length类型:u2
length字节地址:0000000E~0000000F
length值:0x0012
将0x0012转换为十进制,计算获得18。
bytes类型:u1
bytes字节地址:00000010~00000021(length代表地址范围为18个字节)
bytes值:下方图片浅蓝底对应的全部字节内容
经过WinHex查看,对应内容为chapter6/TestClass,即类的全限定名。
经过逐个字节对照ASCII字符表,咱们一样能够获得内容为chapter6/TestClass。
例如0x63为c,0x68为h,0x61为a,0x70为p,0x74为t,0x65为e,0x72为r,连起来表明单词chapter。
tag类型:u1
tag字节地址:00000022
tag值:0x07
这个常量属于CONSTANT_Class_info结构,表明一个类或者接口的符号引用。
name_index类型:u2
name_index字节地址:00000023~00000024
name_index值:0x0004
0x0004指向了常量池中的第4项常量。
tag类型:u1
tag字节地址:00000025
tag值:0x01
这个常量属于CONSTANT_Utf8_info结构,表明一个UTF-8编码的字符串。
length类型:u2
length字节地址:00000026~00000027
length值:0x0010
将0x0010转换为十进制,计算获得16。
bytes类型:u1
bytes字节地址:00000028~00000037(length代表地址范围为16个字节)
bytes值:下方图片浅蓝底对应的全部字节内容
经过WinHex查看,对应内容为java/lang/Object,即类的全限定名。
tag类型:u1
tag字节地址:00000038
tag值:0x01
这个常量属于CONSTANT_Utf8_info结构,表明一个UTF-8编码的字符串。
length类型:u2
length字节地址:00000039~0000003A
length值:0x0001
bytes类型:u1
bytes字节地址:0000003B(length代表地址范围为1个字节)
bytes值:0x6D
经过WinHex查看,对应内容为实例变量m。
其余常量能够经过相似的方法进行分析,但这样一个个分析确实挺辛苦的。
其实,JDK已经为咱们提供了一个Class文件字节码工具:javap,可让咱们较为直观的看到Class文件的字节码内容。
执行命令:javap -verbose TestClass.class,截取常量池部份内容以下:
能够看到,版本号及前5个常量与咱们分析的结果是一致的。因此,能用1行代码搞定的事儿,就不要用2行(浪费笔墨)。
常量池最后一个字节:000000D8
类型:u2
字节地址:000000D9~000000DA
值:0x0021
查看类或接口访问标志含义表可知,该类的访问标志为ACC_PUBLIC(0x0001)、ACC_SUPER(0x0020)。
另外,经过类的定义public class TestClass,一样能够推断出类的访问标志为ACC_PUBLIC、ACC_SUPER,而ACC_INTERFACE、ACC_ENUM、ACC_FINAL、ACC_ABSTRACT、ACC_ANNOTATION、ACC_SYNTHETIC均可以排除。
因此,access_flags应该为0x0001|0x0020=0x0021,结果与查看字节码相同。
类型:u2
字节地址:000000DB~000000DC
值:0x0001
this_class指向常量池的第1个常量,基于前面的分析可知:
所以,类索引(this_class)指向的类为chapter6/TestClass。
类型:u2
字节地址:000000DD~000000DE
值:0x0003
一样,super_class指向常量池的第3个常量。
所以,父类索引(super_class)指向的类为java/lang/Object。
类型:u2
字节地址:000000DF~000000E0
值:0x0000
接口计数器值为0,说明该类没有实现任何接口。
无
类索引(this_class)、父类索引(super_class)和接口索引(interfaces)这三项数据共同肯定了当前类以及其继承关系,相关常量池内容以下:
完整地址范围:000000DB~000000E0
类型:u2
字节地址:000000E1~000000E2
值:0x0001
说明当前类有1个字段。
访问标志(access_flags)
类型:u2
字节地址:000000E3~000000E4
值:0x0002
对应的访问标志为ACC_PRIVATE。
名称索引(name_index)
类型:u2
字节地址:000000E5~000000E6
值:0x0005
对应常量池中的第5项常量,即字段名为m。
描述符(descriptor_index)
类型:u2
字节地址:000000E7~000000E8
值:0x0006
对应常量池中的第6项常量,值为I,即int类型。
所以,该字段的定义为private int m;
类型:u2
字节地址:000000E9~000000EA
值:0x0000
说明该字段没有属性信息。
无。
字段完整地址范围:000000E1~000000EA
最后是方法和属性,因为内容复杂度及篇幅缘由,咱们下篇再续。
参考
《Java虚拟机规范》(Java SE 8版)
《深刻理解Java虚拟机 JVM高级特性与最佳实践》
更多文章,请关注公众号:二进制之路