Class文件解析

本文详细介绍了JavaClass文件的结构,包括魔数、版本号、常量池、访问标志、字段和方法描述、Code属性以及LineNumberTable和LocalVariableTable等调试信息。Class文件由字节流组成,包含了类或接口的所有定义信息,常量池存储各种常量和引用,Code属性则包含实际的字节码指令。
摘要由CSDN通过智能技术生成

目录

Class文件格式总览

常量池(Constant Pool)

数据类型描述规则

成员变量描述规则

成员函数描述规则

字段和方法的数据结构

access_flags

attribute_info

Code属性

LineNumberTable_attribute 

 LocalVariableTable


Class文件格式总览

每一个class文件都对应着唯一一个类或接口的定义信息。

每个class文件都是由字节流组成,每个字节含有8个二进制位,多字节数据项总是按照big-endian(大端模式)的顺序存储。

u4表示4字节

ClassFile {
u4            magic 魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的class文件——魔数值固定(0xCAFEBABE)
u2            minor_version 副版本号
u2            major_version 主版本号
u2            constant_pool_count 常量池计数器   常量池表的索引值只有在大于0小于constant_pool_count才认为有效。
cp_info       constant_pool 常量池  包含class文件结构及其子结构中引用的所有字符串常量,类或接口名和字段名,其它常量。
u2            access_flags 访问标志 由一组标志所构成的掩码,用于表示某个类或接口的访问权限和属性
u2            this_class 类索引,指向类名  必须是对常量池表中某项的一个有效索引值。
u2            super_class 父类索引,指向父类类名 要么是0(超类Object),要么是常量池表中某项的一个有效索引值。
u2            interface_count 接口计数器,表示当前类或接口的直接超接口数量
u2            interfaces[] 接口表 每个成员的值都必须是对常量池表中某项的有效索引值。接口顺序和对应源代码中给定的接口顺序一样。
u2            fields_count 字段计数器,表示当前class文件的fields表的成员个数
field_info    fields[] 字段表 该类和接口所声明的类字段或者实例字段,不包括父类或接口
u2            method_count 方法计数器,表示当前class文件methods表的成员个数
method_info   methods[] 方法表,当前类中方法。不包含超类
u2            attributes_count 属性计数器
attribute_info     attributes[]
}

常量池(Constant Pool)

对应的数据结构伪代码就是一个类型为cp_info的数组。每一个cp_info对象存储了一个常量项。

cp_info {         //u1表示该域对应一个字节长度,u表示unsigned
    u1 tag;       //每一个cp_info的第一个字节表明该常量项的类型
    u1 info[];    //常量项的具体内容
}

常量项的类型

 常量项的类型结构

CONSTANT_Utf8_info{
    u1 tag;
    u2 length;
    u1 bytes[length];
}
CONSTANT_String_info{
    u1 tag;
    u2 string_index;    //index索引到一个Utf8_info元素 ,利用索引可以减少Class文件占用空间
}
CONSTANT_Class_info{
    u1 tag;
    u2 name_index;      
}
CONSTANT_MethodType_info{
    u1 tag;
    u2 descriptor_index;    
}
CONSTANT_Fieldref_info{
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;  
} 
CONSTANT_Methodref_info{
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info{
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
CONSTANT_Long_info{
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
CONSTANT_Double_info{
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
CONSTANT_Interger_info{
    u1 tag;
    u4 bytes;
}
CONSTANT_Float_info{
    u1 tag;
    u4 bytes;
}

数据类型描述规则

1.原始数据类型:"B""C""D""F""I""J""S""Z",分别对应的Java类型为byte、char、double、float、int、long、short、boolean。

2.引用数据类型:"LClassName;",ClassName为对应类的全路径名,比如上"Ljava/lang/String;"

3.数组类型:"[其他类型的描述名",比如一个int数组的描述为"[I",一个字符串数组的描述为"[Ljava/lang/String;",一个二维int数组的描述为"[[I"。

成员变量描述规则

成员变量描述只包含FieldType一种信息,FieldType为数据类型

成员函数描述规则

函数描述包括两个部分,括号内的是参数的数据类型描述,可有0到多个,紧接右括号的是返回值类型描述

如:(Ljava/lang/String;)V。

字段和方法的数据结构

field_info{
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}
Method_info{
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

access_flags

 

 

attribute_info

属性也分很多类型,由其名称来区别

attribute_info {
    u2 attribute_name_index;   // 属性名称,指向常量池中Utf8常量项的索引
    u4 attribute_length;       // 该属性具体内容的长度,即下面info数组的长度
    u1 info[attribute_length]; // 属性具体内容
}

常见的属性 

Code属性

Code_attribute {
         u2 attribute_name_index;   //指向内容为"Code"的Utf8_info常量项
         u4 attribute_length;       //接下来内容的长度
         u2 max_stack;              //说明这个函数在执行过程中,需要最深多少栈空间
         u2 max_locals;             //该函数包括最多几个局部变量
         u4 code_length;
         u1 code[code_length];      //函数的源码经过编译器转换后得到的Java指令码存储在code数组
         u2 exception_table_length; //一个try/catch语句对应exception_table数组中的一项 
         {          u2 start_pc;    //描述try/cath语句从哪条指令开始,指向code数组某处指令
                    u2 end_pc;      //try语句到哪条指令结束
                    u2  handler_pc; //try语句到哪条指令结束
                    u2  catch_type; //catch中截获的Exception或Error的名字,指向Utf8_info常量项。如果catch_type取值为0,则表示它是final{}语句块
         }        exception_table[exception_table_length];
         u2 attributes_count;       //还能包含其他属性
         attribute_info attributes[attributes_count];

    }

Code_attribute里还能包含的其他常见属性有:

·LineNumberTable用于调试,比如指明哪条指令。对应于源码哪一行。

·LocalVariableTable用于调试,调试时可以用于计算本地变量的值。

·LocalVariableTypeTable,功能和LocalVariableTable类似。

·StackMapTable为Java 1.6以上才支持的属性。JVM加载Class文件的时候,将利用该属性的内容对函数进行类型校验(Type Checking)。

 code数组只能包括Java指令码和操作数

JVM规范中定义的Java指令码的个数不超过255个

Java指令码长度为一个字节。指令码后面跟0或多个参数(占N个字节大小,N>=0)。由规范定义好。

LineNumberTable_attribute 

LineNumberTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 line_number_table_length;
    {   u2 start_pc;    //指向Code_attribute中code数组某处指令
        u2 line_number;	//说明start_pc位于源码的哪一行
    } line_number_table[line_number_table_length];
}

 LocalVariableTable

LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {   u2 start_pc;   //局部变量在code数组中的有效起始
        u2 length;     //局部变量在code数组中的有效范围
        u2 name_index; //此局部变量的名字,指向Utf8_info常量项
        u2 descriptor_index; //此局部变量的类型,也指向Utf8_info常量项
        u2 index;
    } local_variable_table[local_variable_table_length];
}

每个非static函数都会自动创建一个叫作this的本地变量,代表当前是在哪个对象上调用此函数。注意,this对象位于局部变量数组第1个位置(Slot=0)。this作用范围贯穿整个函数,所以从Start=0开始,作用范围为Length=20。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值