Class的二进制文件分析—魔法书、版本号、常量池
1.代码
public class Man {
private String name = "edwardy";
private Integer age = 28;
public void outPrint(){
System.out.println("name : "+name+" age: "+age);
}
public static void main(String[] args) {
Man man = new Man();
man.outPrint();
}
}
2.NotePad++安装插件
参考链接:https://blog.csdn.net/Eric_Blog_CSDN/article/details/78904679
3.二进制文件
下图便是该java代码对应的二进制文件
4.二进制解析
4.1. 魔法数
所有编译过后的class文件的前4字节便是魔法数,其显示的十六值是ca fe ba be。JVM在解析的时候,
解析前四个字节不是0xcafebe则会拒绝解析该文件,会认定该文件不是class文件
当前4字节:当前文件前四个字节是cafebabe,因此是一个class文件
4.2. 版本号
版本号分为主版本号(major version)和次版本号(minor version),分别占用两个字节,共占用四个字节。
在编译的时候,会将jdk的版本信息对应的写入这些字节中。因此,有时候可以通过class文件中的版本号返推出该文件编译所
使用的jdk版本
JDK和版本号之间的对应关系:
JDK 1.8 = 52
JDK 1.7 = 51
JDK 1.6 = 50
JDK 1.5 = 49
JDK 1.4 = 48
JDK 1.3 = 47
JDK 1.2 = 46
JDK 1.1 = 45
当前4字节:00 00 00 33。00 00(主版本号);00 33(次本号,33的十进制是51 因此对应的jdk版本号是1.7)
4.3. 常量池
4.3.1. 常量池的元素结构
常量总数|常量元素1|常量元素2|常量元素3|......|常量元素N(一个常量元素= tag表示+元素内容)
4.3.2. 常量池元素结构表
该表摘自揭秘java虚拟机
类型 | 结构 | 类型 | 描述 |
---|---|---|---|
CONSTANT_Utf8_info | tag | u1 | 值为1 |
‘’ | length | u2 | UF-8编码的字符串占用的字节数 |
” | bytes | u2 | 长度为length的UTF-8编码的字符串 |
CONSTANT_Integer_info | tag | u1 | 值为3 |
” | bytes | u4 | 按照高位在前存储的int值 |
CONSTANT_Float_info | tag | u1 | 值为4 |
” | bytes | u4 | 按照高位在前存储的float值 |
CONSTANT_Float_info | tag | u1 | 值为5 |
” | bytes | u8 | 按照高位在前存储的long值 |
CONSTANT_Double_info | tag | u1 | 值为6 |
” | bytes | u8 | 按照高位在前存储的double值 |
CONSTANT_Class_info | tag | u1 | 值为7 |
” | index | u2 | 指向全限定名常量项的索引 |
CONSTANT_String_info | tag | u1 | 值为8 |
” | index | u2 | 指向字符串字面量的索引 |
CONSTANT_String_info | tag | u1 | 值为9 |
” | index | u2 | 指向声明字段的类或接口描述符CONSTANT_Class_info的索引项 |
” | index | u2 | 指向字段名称及类型描述符CONSTANT_NameAndType_info的索引项 |
CONSTANT_Methodref_info | tag | u1 | 值为10 |
” | index | u2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 |
” | index | u2 | 指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项 |
CONSTANT_Methodref_info | tag | u1 | 值为11 |
” | index | u2 | 指向声明方法的接口描述符CONSTANT_Class_info的索引项 |
” | index | u2 | 指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项 |
CONSTANT_NameAndType_info | tag | u1 | 值为12 |
” | index | u2 | 指向字段或方法名称常量项目的索引 ” |
4.3.3. 元素总数
在次版本号之后的两个字节便是记录了常量池元素的长度,共8位。在当前例子中,该两位字节是00 48,换算成十进制是
72,即常量池的的元素个数72,接下来开始从第一个元素开始进行解析(JVM规定:不使用第0个元素,因此实际上有71个常量元素
4.3.4. 第1个常量池元素解析
Tag解析: 在常量池长度00 48的十六进制之后,是第一个元素的tag解析,tag长度占用一个字节,即十六进制为07。查看4.3.2的表可知,0707代表是
CONSTANT_Class_info类或接口的符号引用。
元素内容解析: 由4.3.2表可知,tag为7的元素内容为U2,占用两个字节,代表的内容式指向字符串字面量的索引。对应该例子,十六进制为00 02
4.3.5. 第2个常量池元素解析
Tag解析: 十六进制为01,对应表的内容为CONSTANT_UTF8_INFO即UTF-8编码的字符串
Length解析: 根据表可知,该元素结构有长度属性,且式u2占两个字节,因此对应的十六进制为00 03,换算成十进制为3,意味着字符串长度为3个字节
元素内容: 根据表可知,内容占U1即一个字节的字符串,十六进制4d 61 6e,每两个字节一个字符,即对应的十进制分别式77(M) 97(a) 110(n),
对应的字符串翻译为Man
4.3.6. 父类常量(第三、四个常量池元素分析)
第三和第四个个常量池元素的作用是描述父类信息,由于Man类没有继承任何类,因此会默认继承object类
第3个元素分析
Tag解析: 十六进制为07,对应表的内容为CONSTANT_Class_info即类或接口的符号引用
元素内容: 根据表可知,后面两个字节式全限定名常量项的索引。对应的字节内容式00 04
第4个元素分析
Tag解析: 十六进制为01,对应表的内容为CONSTANT_Utf-8_info编码的字符串
Length解析: 根据表可知,该元素结构有长度属性,且式u2占两个字节,因此对应的十六进制为00 10,换算成十进制为16,意味着字符串长度为16个字节
元素内容: 根据表可知,后面16个字节对应字符串内容给。对应的字节内容式6a(106) 61(97) 76(118) 61(97) 2f(47) 6c(108) 61(97) 6e(110) 67(103) 2f(47) 4f(79) 62(98) 6a(106) 65(101) 63(99) 74(116),
最后将这些十进制对应ASCII表,翻译出来的字符串为java/lang/object
4.3.7. 变量型常量池元素
变量型常量池描述的是当前类中的变量信息,包括成员变量和静态变量
第5个元素分析(01类型)
Tag解析: 十六进制为01,对应表的内容为CONSTANT_Utf-8_info编码的字符串
Length解析: 根据表可知,该元素结构有长度属性,且式u2占两个字节,因此对应的十六进制为00 04,意味着字符串长度为04个字节
元素内容: 根据表可知,后面4个字节对应字符串内容给。对应的字节内容是6e(110) 61(97) 76(118) 6d(109) 65(101),最后将这些十进制对应ASCII表,翻译出来的字符串为name变量
第6个元素分析(01类型)
Tag解析: 十六进制为01,对应表的内容为CONSTANT_Utf-8_info编码的字符串
Length解析: 根据表可知,该元素结构有长度属性,且式u2占两个字节,因此对应的十六进制为00 12,意味着字符串长度为18个字节
元素内容: 根据表可知,后面18个字节对应字符串内容是4c(76) 6a(106) 61(97) 76(118) 61(97) 2f(47) 6c(108) 61(97) 6e(110) 67(103) 2f(47) 53(83) 74(116) 72(114) 69(105) 6e(110) 67(103) 3b(59)。对应的字节内容是,最后将这些十进制对应ASCII表,翻译出来的字符串为
Ljava/lang/String;可以看出第五个元素是变量名,而第六个元素是第五个元素的变量类型,这两个元素组合成成员变量name
第7个元素分析(01类型)
Tag解析: 十六进制为01,对应表的内容为CONSTANT_Utf-8_info编码的字符串
Length解析: 根据表可知,该元素结构有长度属性,且式u2占两个字节,因此对应的十六进制为00 04,意味着字符串长度为04个字节
元素内容: 根据表可知,后面3个字节对应字符串内容给。对应的字节内容是61(97) 67(103) 65(101),最后将这些十进制对应ASCII表,翻译出来的字符串为age变量
第8个元素分析(01类型)
Tag解析: 十六进制为01,对应表的内容为CONSTANT_Utf-8_info编码的字符串
Length解析: 根据表可知,该元素结构有长度属性,且式u2占两个字节,因此对应的十六进制为00 12,意味着字符串长度为18个字节
元素内容: 根据表可知,后面18个字节对应字符串内容是Ljava/lang/Integer; 可以看七八两个元素组合成成员变量age
第9元素分析(01类型)
字符串:<init>
,这个元素的含义是指Java编译器把所有的类变量初始化语句和类型的静态初始化器通通收集到 方法内,该方法只能被 Jvm 调用,专门承担初始化工作,该元素的详细解释见下面的链接:
https://blog.csdn.net/jamse19860909/article/details/7210244
第10元素分析(01类型)
字符串:()V
第11元素分析(01类型)
字符串:code
第12个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 03,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项3
index:查表可知占两个字节00 0d,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项13
第13个元素分析(13类型)
Tag解析:一个字节,十六进制是0c,对应的是十进制为13的字段或方法的符号引用
index:查看表可知占两个字节00 09,字段或方法名称常量项的索引项9
index:查表可知占两个字节00 0a,字段或方法描述符常量项的索引项10
第14个元素分析(08类型)
Tag解析:一个字节,十六进制是08,字符串类型字面量
index:两个字节的字节码为0f,指向字符串字面量的索引为15
第15个元素分析(01类型)
字符串:edwardy
第16个元素分析(09类型)
Tag解析:一个字节,十六进制是09,对应的是字段的符号引用
index:查看表可知占两个字节00 01,字段的类或接口描述符的索引1
index:查表可知占两个字节00 11,字段描述符的索引17
第17个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 05,字段或方法名称常量项的索引5
index:占两个字节00 16,字段或方法描述符常量项的索引6
第18个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 13,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项19
index:查表可知占两个字节00 15,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项21
第19个元素分析(07类型)
Tag解析:一个字节,十六进制是07,对应的是十进制为07的常量池元素结构为类或接口的符号引用
index:查看表可知占两个字节00 14,含义是指向全限定名常量池的索引20
第20个元素分析(01类型)
字符串:java/lang/Integer
第21个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 16,字段或方法名称常量项的索引22
index:占两个字节00 17,字段或方法描述符常量项的索引23
第22个元素分析(01类型)
字符串:valueOf
第23个元素分析(01类型)
字符串:(I)Ljava/lang/Integer;
第24个元素分析(09类型)
Tag解析:一个字节,十六进制是09,对应的是字段的符号引用
index:查看表可知占两个字节00 01,字段的类或接口描述符的索引1
index:查表可知占两个字节00 19,字段描述符的索引25
第25个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 07,字段或方法名称常量项的索引07
index:占两个字节00 08,字段或方法描述符常量项的索引08
第26个元素分析(01类型)
字符串: LineNumberTable
第27个元素分析(01类型)
字符串: LocalVariableTable
第28个元素分析(01类型)
字符串: this
第29个元素分析(01类型)
字符串: LMain
第30个元素分析(01类型)
字符串: outPrint
第31个元素分析(09类型)
Tag解析:一个字节,十六进制是09,对应的是字段的符号引用
index:查看表可知占两个字节00 20,字段的类或接口描述符的索引32
index:查表可知占两个字节00 22,字段描述符的索引34
第32个元素分析(07类型)
Tag解析:一个字节,十六进制是07,对应的是十进制为07的常量池元素结构为类或接口的符号引用
index:查看表可知占两个字节00 21,含义是指向全限定名常量池的索引33
第33个元素分析(01类型)
字符串: java/lang/System
第34个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 23,字段或方法名称常量项的索引35
index:占两个字节00 24,字段或方法描述符常量项的索引36
第35个元素分析(01类型)
字符串: out
第36个元素分析(01类型)
字符串: Ljava/io/PrintStream;
第37个元素分析(07类型)
Tag解析:一个字节,十六进制是07,对应的是十进制为07的常量池元素结构为类或接口的符号引用
index:查看表可知占两个字节00 26,含义是指向全限定名常量池的索引38
第38个元素分析(01类型)
字符串:java/lang/StringBuilder
第39个元素分析(08类型)
Tag解析:一个字节,十六进制是08,字符串类型字面量
index:两个字节的字节码为28,指向字符串字面量的索引为40
第40个元素分析(01类型)
字符串:name : (冒号前后有个ascii为32的空值)
第41个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 25,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项37
index:查表可知占两个字节00 2a,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项42
第42个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 09,字段或方法名称常量项的索引9
index:占两个字节00 2b,字段或方法描述符常量项的索引43
第43个元素分析(01类型)
字符串:(Ljava/lang/String;)v
第44个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 25,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项37
index:查表可知占两个字节00 2d,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项45
第45个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 2e,字段或方法名称常量项的索引46
index:占两个字节00 2f,字段或方法描述符常量项的索引47
第46个元素分析(01类型)
字符串:append
第47个元素分析(01类型)
字符串:(Ljava/lang/string;)Ljava/lang/StringBuilder;
第48个元素分析(08类型)
Tag解析:一个字节,十六进制是08,字符串类型字面量
index:两个字节的字节码为31,指向字符串字面量的索引为49
第49个元素分析(01类型)
字符串: age: (该字符串一前一后有个ascii为32的空值)
第50个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 25,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项37
index:查表可知占两个字节00 33,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项51
第51个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 2e,字段或方法名称常量项的索引46
index:占两个字节00 34,字段或方法描述符常量项的索引51
第52个元素分析(01类型)
字符串: (Ljava/lang/Object;)Ljava/lang/StringBuilder
第53个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 25,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项37
index:查表可知占两个字节00 36,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项54
第54个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 37,字段或方法名称常量项的索引55
index:占两个字节00 38,字段或方法描述符常量项的索引56
第55个元素分析(01类型)
字符串: toString
第56个元素分析(01类型)
字符串: ()Ljava/lang/String;
第57个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 3a,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项58
index:查表可知占两个字节00 3c,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项60
第58个元素分析(07类型)
Tag解析:一个字节,十六进制是07,对应的是十进制为07的常量池元素结构为类或接口的符号引用
index:查看表可知占两个字节00 3b,含义是指向全限定名常量池的索引49
第59个元素分析(01类型)
字符串: java/io/PrintStream
第60个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 3d,字段或方法名称常量项的索引61
index:占两个字节00 2b,字段或方法描述符常量项的索引43
第61个元素分析(01类型)
字符串: println
第62个元素分析(01类型)
字符串: main
第63个元素分析(01类型)
字符串: ([Ljava/lang/String;)v
第64个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 01,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项1
index:查表可知占两个字节00 0d,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项13
第65个元素分析(10类型)
Tag解析:一个字节,十六进制是0a,对应的是十进制为10的常量池元素结构为类中方法的符号引用
index:查看表可知占两个字节00 01,含义是指向声明方法的类描述符CONSTANT_CLASS_INFO的索引项1
index:查表可知占两个字节00 42,含义是指向名称及类型描述符CONSTANT_NameAndType_info的索引项66
第66个元素分析(12类型)
Tag解析:一个字节,十六进制是0c,字段或方法的部分符号引用
index:占两个字节00 1e,字段或方法名称常量项的索引30
index:占两个字节00 0a,字段或方法描述符常量项的索引10
第67个元素分析(01类型)
字符串: args
第68个元素分析(01类型)
字符串: [Ljava/lang/String;
第69个元素分析(01类型)
字符串: man
第70个元素分析(01类型)
字符串: SourceFile
第71个元素分析(01类型)
字符串: Man.java
4.4. 总结
(1)在第71一个元素之后的字节码是00 21,00没有上表中没有对应的元素类型,即代表常量元素的解析结束。之后的解析是访问标识和继承信息,将会在下一章学习中记录
(2)在4.3.3中元素总数的字节码解析时,总共72个(JVM规定:不使用第0个元素),实际有效的是71。通过对后面的字节码的耐心解析,最后发现的的确确是71个常量元素
(3)除了二进制分析class之外,还可以通过javap命令进行分析cl,可以结合解析之后的图来对比二进制的文件解析
4.5. 结语
常量池的解析暂时的理解只有这些,分析二进制的时候仍然有很多不明白的细节,比如为什么很多类型前面加L,()V是什么含义有什么作用。在后面对JVM继续了解学习之后,再回来更新!
文章写的较为匆忙,有很多遗漏的地方,忘大神指正,勿喷!