JVM study notes - the class file structure

  Java proposed a "write once, run anywhere" slogan, the same program can run on a different platform, the basis of language-independent virtual machine storage and bytecode format, and Java virtual machine does not include Java, including any language bindings. The "depth understanding of the Java Virtual Machine" content below as "Java Virtual Machine Specification (2nd Edition)", the class file corresponding structures described JDK1.4 Java virtual machine.

1, Class Class File Structure

  Class file is a set of 8-bit binary stream in units of bytes based on respective data item in strict accordance with the order of the compact, without any intermediate separator.

1.1 data structures and file formats

  Class file only two kinds of data structures: unsigned and tables, unsigned data type is basic to u1, u2, u4, u8 represents 1 byte, 2 bytes, 4 bytes, 8 unsigned number, while the table is an unsigned number, and other configuration tables bytes, the entire Class file is essentially a table. The following is a data item Class file:

Types of name Quantity
u4 magic 1
u2 minor_version 1
u2 major_version 1
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count
u2 access_flags 1
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attributes_count 1
attributes_info attributes attributes_count

  Class file byte length, representational, and other details of the order are strict rules can not be changed. The following describes the specific data items.

1.2 magic number and file versions Class

  Class file before the magic number is four bytes for identification, it is determined whether the file is a virtual machine accepted. Magic number of Java Class files is 0xCAFEBABE.

  接下来的是Class文件的次版本号和主版本号。次版本号是小数点后面的部分,主版本号是小数点前面的部分。比如JDK1.2支持45.0~46.65535,如果有个Class文件的版本号是45.00,那么次版本号就是0,主版本号是45。

1.3 常量池

   constant_pool_count是Class文件常量池中常量的数量,如果这个数值是6,说明常量池中有5个常量,因为第0个是空出来用于表达"不引用任何一个常量池项目"的含义。常量池中主要放2类常量:字面量和符号引用。其中符号引用有下面3类:

1、类和接口的全限定名:把类的全名由"."改为"/"

2、字段的名称和描述符:

3、方法的名称和描述符:

  Class文件中字段和方法的符号引用需要经过运行期转换后才能得到真正的内存入口地址,当虚拟机运行时,从常量池获取对应的符号引用,然后在类创建或运行时解析、翻译到具体的内存地址。JDK1.7中一共有下面14种常量:

类型 标志 描述
CONSTANT_Utf8_info 1 UTF-8编码的字符串
CONSTANT_Integer_info 3 整型字面量
CONSTANT_Float_info 4 浮点型字面量
CONSTANT_Long_info 5 长整型字面量
CONSTANT_Double_info 6 双精度浮点型字面量
CONSTANT_Class_info 7 类或接口的符号引用
CONSTANT_String_info 8 字符串类型字面量
CONSTANT_Fieldref_info 9 字段的符号引用
CONSTANT_Methodref_info 10 类中方法的符号引用
CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用
CONSTANT_MethodHandler_info 15 表示方法句柄
CONSTANT_MethodType_info 16 表示方法类型
CONSTANT_InvokeDynamic_info 18 表示一个动态方法调用点

  这14种常量的表的第一位都是一个u1类型的标志位,用于表示常量类型。引用书中的Java代码进行分析:

package or.fenixsoft.clazz;

public class TestClass {
    private int m;
    public int inc(){
        return m + 1;
    }
}

  对应解析出来的Class文件字节码:

......
Constant pool:
const #1 = class                #2;  //org/fenixsoft/clazz/TestClass
const #2 = Asciz                org/fenixsoft/clazz/TestClass;
const #3 = class                #4;  //java/lang/Object
const #4 = Asciz                java/lang/Object
const #5 = Asciz                m;
const #6 = Asciz                I;
const #7 = Asciz                <init>;
const #8 = Asciz                ()V;
const #9 = Asciz                Code;
const #10 = Method              #3.#11;  //java/lang/Object."<init>":()V
const #11 = NameAndType         #7:#8;  //"<init>":()V
const #12 = Asciz               LineNumberTable;
const #13 = Asciz               LocalVariableTable
const #14 = Asciz               this;
const #15 = Asciz               Lorg/fenixsoft/clazz/TestClass;;
const #16 = Asciz               inc;
const #17 = Asciz               ()I;
const #18 = Field               #1.#19;  //org/fenixsoft/clazz/Testclass.m:I
const #19 = NameAndType         #5:#6;  //m:I
const #20 = Asciz               SourceFile;
const #21 = Asciz               TestClass.java;

   下面是对应的常量池结构:

1 CA FE BA BE 00 00 00 32
2 00 16 07 00 02 01 00 1D
3 6F 72 67 2F 66 65 6E 69
4 78 73 6F 66 74 2F 63 6C
5 ......

  第二行的07开始就是常量池项目,它的常量结构如下表:

类型 名称 数量
u1 tag 1
u2 name_index 1

  7对应的是CONSTANT_Class_info,接下来的0002表示值为第二个常量。第二个常量是01,即UTF-8编码的字符串,它的结构为:

类型 名称 数量
u1 tag 1
u2 length 1
u1 bytes length

  001D即是29,接下来29个字节组成的字符串即是第二个变量代表的字符串。CONSTANT_Utf8_info的长度类型是u2,代表字段名、方法名不能超过65535,正常也不会取这么长的名字吧。其他常量格式不给出,都是按照这样的方式存储的。

1.4 访问标志

  访问标志用于标识类或接口层次的访问信息,16个标志位定义了8个,没有使用到的一致为0:

标志名称 标志值 含义
ACC_PUBLIC 0x0001 是否为public类型
ACC_FINAL 0x0010 是否声明为final
ACC_SUPER 0x0020 是否允许使用invokespecial指令新语义
ACC_INTERFACE 0x0200 是否是接口
ACC_ABSTRACT 0x0400 是否是abstract类型
ACC_SYNTHETIC 0x1000 标识类非由用户代码产生
ACC_ANNOTATION 0x2000 是否是注解
ACC_ENUM 0x4000 是否是枚举

1.5 类索引、父类索引、接口索引集合

  this_class是类的全限定名,super_class是父类的全限定名,接口集合则是根据implements后面的接口顺序从左到右排序。这几项都指向CONSTANT_Class_info常量。

1.6 字段表集合

  字段表用于描述类或接口的变量,方法内部定义的局部变量不包含在内。下面是fields字段表的结构:

类型 名称 数量
u2 access_flags 1
u2 name_index 1
u2 descriptor_index 1
u2 attributes_count 1
attribute_info attributes attributes_count

1.6.1 access_flags

  字段修饰符放在access_flags中,具体如下表:

标志名称 标志值 含义
ACC_PUBLIC 0x0001 字段是否public
ACC_PRIVATE 0x0002 字段是否private
ACC_PROTECTED 0x0004 字段是否protected
ACC_STATIC 0x0008 字段是否static
ACC_FINAL 0x0010 字段是否final
ACC_VOLATILE 0x0040 字段是否volatile
ACC_TRANSIENT 0x0080 字段是否transient
ACC_SYNTHETIC 0x1000 字段是否由编译器自动产生
ACC_ENUM 0x4000 字段是否enum

1.6.2 name_index

  这个表示字段的简单名称,简单名称是指没有类型和参数修饰的方法或字段名,比如常用的有个toString的简单名称。

1.6.3 descriptor_index

  描述符,用于描述字段的数据类型、方法的参数列表和返回值。基本数据类型和代表无返回值的void类型用一个大写字符表示,而对象类型则用字符L加对象的全限定名来表示,如下表:

标识字符 含义
B 基本类型byte
C 基本类型char
D 基本类型double
F 基本类型float
I 基本类型int
J 基本类型long
S 基本类型short
Z 基本类型boolean
V 特殊类型void
L 对象类型

  对于数组类型,每一个维度用"["表示,如定义一个"java.lang/String[][]"类型,则是"[[Ljava/lang/String",一个整型数组"int[]",则是"[I"。

1.6.4 attributes

  属性表,用来存储一些额外的信息,如果有字段"final static int m = 123",则会存在一个名称为ConstantValue的属性,指向常量123。

1.7 方发表集合

  方法表的结构和字段表一致,依次包括access_flags、name_index、descriptor_index、attributes_count、attributes。方法表集合可能会有编译器自动添加的方法,比如类构造器"<clinit>"方法和实例构造器"<init>"方法。

1、类构造器:编译器自动收集类中所有类变量和静态语句块中的语句合并而成,顺序为源代码顺序。

2、实例构造器:编译器自动收集类中所有实例变量和非静态语句块中的语句合并而成。

1.7.1 access_flags

  方法表的访问标志与字段有所区别:

标志名称 标志值 含义
ACC_PUBLIC 0x0001 方法是否为public
ACC_PRIVATE 0x0002 方法是否为private
ACC_PROTECTED 0x0004 方法是否为protected
ACC_STATIC 0x0008 方法是否为static
ACC_FINAL 0x0010 方法是否为final
ACC_SYNCHRONIZED 0x0020 方法是否为synchronized
ACC_BRODGE 0x0040 方法是否为由编译器产生的桥接方法
ACC_VARARGS 0x0080 方法是否接受不定参数
ACC_NATIVE 0x0100 方法是否为native
ACC_ABSTRACT 0x0400 方法是否为sbstract
ACC_STRICTFP 0x0800 方法是否为strictfp
ACC_SYNTHETIC 0x1000 方法是否为编译器自动产生的

1.7.2 descriptor_index

  描述符先按照参数列表,后返回值的顺序描述,参数列表按照参数顺序方法小括号"()"中,方法void inc(int a,char b)的描述符为"(IC)V",java.lang.String.toString()的,描述符为"()Ljava/lang/String"

1.7.3 attributes

  方法里的Java代码会存放在属性表中,在一个"Code"的属性中。

1.8 属性表集合

  在Class文件、字段名和方法表中都可以携带自己的属性表集合,用于描述某些场景专有的信息。

1.8.1 Code属性

  Code属性就是方法表中存放方法代码的地方。Code属性结构为:

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 max_stack 1
u2 max_locals 1
u4 code_length 1
u1 code_length code_length
u2 exception_table_length 1
exception_info exception_table exception_table_length
u2 attributes_count 1
attribute_info attributes attributes_count

1、attribute_name_index

  这个属性项是一个指向CONSTANT_Utf8_info常量的索引,值为"Code",代表属性名称。

2、attribute_length

  属性值的长度,等于整个属性表的长度减去6个字节(属性名和属性长度占据的6个字节)。

3、max_stack

  代表错作数栈深度的最大值,虚拟机运行时根据这个值分配栈帧中的操作栈深度。

4、max_locals

  局部变量表所需的存储空间。单位是Slot。长度不超过32位的数据类型,每个局部变量占用一个Slot,double和long占用2个Slot。局部变量占据Slot的和不等于max_locals,代码执行超出一个局部变量的作用域时,其占据的Slot可以复用。

5、code

  用于存储源代码编译生成的字节码指令,指令是u1类型的单字节。有些指令是带有参数的,虚拟机知道如何理解。另外code_length虽然定义 了u4类型的长度,实际虚拟机规范限制了一个方法的字节码不能超过65535条。

6、exception_table

  方法的异常处理表,它的结构为:

类型 名称 数量
u2 start_pc 1
u2 end_pc 1
u2 handler_pc 1
u2 catch_type 1

  含义为:如果字节码在第start_pc行到end_pc(不包括end_pc)出现了类型为catch_type或其子类的异常,则转到第handler_pc行继续处理。当catch_type的值为0是,任何异常都要转到handler_pc处理。

1.8.2 Exception属性

  这个属性跟Code属性平级,与上面的异常表不同,是方法描述时在throws关键字后面列举的异常。结构为:

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_exception 1
u2 exception_index_table number_of_exception

  exception_index_table表示抛出的受检异常,是一个指向常量池CONSTANT_Class_info型常量的索引。

Guess you like

Origin www.cnblogs.com/liuwy/p/11069594.html