class文件格式:
测试文件:
package org.fenixsoft.clazz;
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}
(一)魔法数0XCAFEBABE:
(二)次 版本号0X00:
(三)主版本号0X33(十进制为49,对应java6):
(四)常量池0X16,表示后续有21个常量:
常量池项目类型表:
(4.1)常量池中第一个常量表:
CONSTANT_class_info型常量结构表:
name_index是一个索引值,它指向常量池中一个CONSTANT_utf8_info类型的常量,此常量代表这个类(或者接口)的全限定名。
测试用class_info标志位:
测试用class_info索引值,0X02指向常量池中第二个常量:
常量池中11种数据类型结构总表:
(4.2)第二项常量的标志位,0X01表示该常量类型为utf8_info类型,此处为常量池中第二个常量表:
utf8_info表的标志位:
第二项常量utf8_info表的长度0X1D(29)
class包名org.fenixsoft.clazz/Testclass;
继续往下分析。。。
(4.3)常量池中第三个常量表(class_info表):
标志位:
0X04表示指向常量池中第四个常量
(4.4)指向CONSTANT_utf8_info常量表,此为常量池中第四个常量表:
长度为16的java/lang/object
(4.5)常量池中第五个常量表(utf8_info表),值为m:
(4.6)常量池中第六个常量表(utf8_info表),值为I:
(4.7)常量池中第七个常量表(utf8_info表),值为<init>:
(4.8) 常量池中第八个常量表(utf8_info表),值为()V:
(4.9) 常量池中第九个常量表(utf8_info表),值为Code:
(4.10) 常量池中第十个常量表(Method_info表):
(4.11) 常量池中第十一个常量表(NameAndType_info表):
(4.12) 常量池中第十二个常量表(utf8_info表),值为LineNumberTable:
(4.13) 常量池中第十三个常量表(utf8_info表),值为LocalVariableTable:
(4.14) 常量池中第十四个常量表(utf8_info表),值为this:
(4.15) 常量池中第十五个常量表(utf8_info表),值为Lorg/fenixsoft/clazz/TestClass; :
(4.16) 常量池中第十六个常量表(utf8_info表),值为inc:
(4.17) 常量池中第十七个常量表(utf8_info表),值为()I:
(4.18) 常量池中第十八个常量表(CONSTANT_field_info表):
(4.19) 常量池中第十九个常量表(CONSTANT_NameAndType_info表):
(4.20) 常量池中第二十个常量表(utf8_info表),值为SourceFile:
(4.21) 常量池中第二十一个常量表:
至此,常量池的表结束。
(五)访问标志位表:
用于确定class是类还是接口,是否定义为public,是否定义为abstract,如果是类的话,是否被声明为final,等
0X21=ACC_PUBLIC | ACC_SUPER(0X0001 | 0X0020=0X0021)
(六)类索引,父类索引,接口索引集合:
类索引查找全限定名的过程:
类索引:
父类索引:
接口索引,本例中,没有接口,所以索引号为0:
(七)字段表结构:
字段表,批注1处,0X01表示该类只有一个字段,即计数器为1,批注2处0X02表示访问标识为ACC_PRIVATE,
批注3处0X05标识name_index指向常量池的第五个常量,即上面所列的CONSTANT_utf8_info字符串m,
批注4处0X06表示descriptor_index的值为0X06,指向常量池的第六个常量,即I,根据这些信息,我们可以推断,
源代码定义的是private int m,批注5处,0X00表示字段m的属性计数器为0,索引额外的信息描述为0X00:
(八)方法表:
方法表结构:
批注1处0X02表示方法表计数器容量为2,代表集合中有两个方法,标注2处0X01访问标志表示ACC_PUBLIC,
标注3处0X07表示名称索引值为7,对应<init>方法,标注4处0X08表示描述符索引为8,对应常量为()V,标注5处
0X01表示属性表计数器为1,表示此方法属性表集合有一项属性,属性名称索引为0X09,对应常量为Code,说明此属性是
方法的字节码
(九)属性表结构:
虚拟机规范的预定义属性
Code属性表结构:
java程序体的代码经过javac编译后,最终变为字节码存储在Code属性内(接口和抽象类没有Code属性),
attribute_name_index是一项指向CONSTANT_utf8_info型常量的索引,常量值固定为Code,attribute_length指示了属性值得长度,属性名称索引
和属性长度一共是6个字节,所以属性值得长度固定为整个属性表的长度减去6个字节。max_stack代表了操作数栈的最大深度,max_locals代表局部变量所需的存储空间,
code用来存储java源程序编译后生成的字节码指令,code_length代表字节码长度(虚拟机规范中规定一个方法不允许超过65535条字节码指令),
属性表集合(属性表总长度为0X2F):
标注1处max_stack代表了操作数栈最大深度值为1,标注2处max_locals本地变量表容量为1,
字节码长度为5
按照顺序,依次读入紧随的5个字节,字节码指令为0X2A,0XB7,0X00,0X0A,0XB1
翻译过程
读入2A,对应指令为aload_0
读入B7,对应指令为invokespecial
读入0X000A,这是invokespecial的参数
读入B1,对应的指令为return
字节码之后是异常表,异常表对于Code属性不不一定是存在的。异常表如图:
意义为如果字节码从start_pc到end_pc间出现了异常类型为catch_type或其子类异常,则转到handler_pc处理。
以下省略Exceptions属性,LineNumberTable属性,LocalVariableTable属性,SourceFile属性,ConstantValue属性,
InnerClass属性,Deprecated和Synthetic属性。