Java基础

class(一) 字节码文件结构

2020-11-21  本文已影响0人  Timmy_zzh
  1. class文件与虚拟机
  2. .class文件内容
  3. 常量池解析(重点掌握)

1.Class字节码文件由来

1.字节码与虚拟机解耦.png

2.class文件组成

class文件里只有两种数据结构:无符号数和表

表和无符号数之间的关系:

2.表和无符号数之间的关系.png

伪代码如下:

// 无符号数
u1 = byte[1];
u2 = byte[2];
u4 = byte[4];
u8 = byte[8];

// 表
class_table {
    // 表中可以引用各种无符号数,
    u1 tag;
    u2 index2;
    ...
    // 表中也可以引用其它表
    method_table mt;
    ...
}

3.class文件结构

魔数 版本号 常量池 访问标志 类/父类/接口 字段描述集合 方法描述集合 属性描述集合

当JVM加载某个class文件时,jvm就是根据上图中的结构去解析class文件,加载class文件到内存中,并在内存中分配相应的空间。具体某一种结构需要占用多大空间,如下图:

字段 名称 数据类型 数量
magic number 魔数 u4 1
major version 主版本号 u2 1
minor version 副版本号 u2 1
constant_pool_count 常量池大小 u2 1
constant_pool 常量池 cp_info constant_pool_count-1
access_flag 访问标志 u2 1
this_class 当前类索引 u2 1
super_class 父类索引 u2 1
interfaces_count 接口索引集合大小 u2 1
interfaces 接口索引集合 u2 interfaces_count
fields_count 字段索引集合大小 u2 1
fields 字段索引集合 field_info fields_count
methods_count 方法索引集合大小 u2 1
methods 方法索引集合 method_info methods_count
attributes_count 属性索引集合大小 u2 1
attributes 属性索引集合 attribute_info attributes_count

实例分析

import java.io.Serializable;

public class Test implements Serializable, Cloneable {

    private int num = 1;
    public int add(int i) {
        int j = 10;
        num = num + i;
        return num;
    }
}
3.class内存(16进制).png 7.class字节码文件1.png 7.class字节码文件2.png
魔数 magic number
CA FE BA BE
版本号
00 00 00 34
常量池

常量池中的每一项都是一个表,其项目类型共有14种,如下表:

表名 标识位(Tag) 描述
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_MethodHandle_info 15 方法句柄表
CONSTANT_MethodType_info 16 方法类型表
CONSTANT_InvokeDynamic_info 18 动态方法调用表

以CONSTANT_Class_info和CONSTANT_utf8_info两张表距离说明:

CONSTANT_Class_info表具体结果如下:

table CONSTANT_Class_info {
    u1  tag = 7;
    u2  name_index;
}

CONSTANT_utf8_info表具体结果如下:

table CONSTANT_utf8_info {
    u1  tag;
    u2  length;
    u1[] bytes;
}

在常量池内部的表中也有相互之间的引用。用一张图来理解CONSTANT_Class_info和CONSTANT_utf8_info表格之间的关系,如图:

4.常量池中表之间的关系.png
接着往下解析,常量池元素个数
5.常量池数量大小.png
第一个常量,如下图
6.常量池第一个常量.png
CONSTANT_Methodref_info {
    u1 tag = 10;
    u2 class_index;        指向此方法的所属类
    u2 name_type_index;    指向此方法的名称和类型
}
6.常量池第一个常量表示.png
接着看第二个常量
8.常量池第二个常量.png
CONSTANT_Fieldref_info{
    u1 tag;
    u2 class_index;        指向此字段的所属类
    u2 name_type_index;    指向此字段的名称和类型
}
8.常量池第二个常量字节码表示.png

其他常量也使用同样方式解析

4.访问标志

9.访问标志.png
访问标志 描述
ACC_PUBLIC 0X0001 public类型
ACC_FINAL 0X0010 被声明为final类型的类
ACC_SUPER 0X0020 是否允许使用invokespecial字节码指令的新语义,默认为真
ACC_INTERFACE 0X0200 标志这时一个接口类型
ACC_ABSTRACT 0X0400 标志这是一个抽象类或是接口类型
ACC_ANNOTATION 0X2000 标志这时一个注解
ACC_ENUM 0X4000 标志这时一个枚举

5.类索引,父类索引与接口索引计数器

在访问标志后的2个字节是类索引,类索引后的2个字节就是父类索引,父类索引后的2个字节则是接口索引计数器,如下图:

10.类索引与父类索引.png

看字节码对应内容:

10.2类-父类-接口索引.png

6.字段表

11.字段表.png

字段表的具体结构为:

CONSTANT_Fieldref_info{
    u2  access_flags        字段的访问标志
    u2  name_index          字段的名称索引(也就是变量名)
    u2  descriptor_index    字段的描述索引(也就是变量的类型)
    u2  attributes_count    属性计数器
    attribute_info
}
字段访问标志
11-1字节码-字段访问标志.png

Java类中的变量,也可以使用public,private,final,static等标识符进行标识。因此解析字段时,需要先判断它的访问标志,字段的访问标志如下:

字段访问标志 描述
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_ENUM 0X4000 字段是否为enum

字段表结构图中的访问标志值为0002,代表它是private类型,变量名索引指向第7的常量;变量名类型索引指向第8个字段。根据class字节码文件内容可以知道,

11-1字节码-字段访问标志.png

7.方法表

字段表之后跟着的就是方法表常量。方法表常量应该也是以一个计数器开始的,因为一个类中的方法数量是不固定的。


12-2.add方法表内容.png 12.方法表.png
CONSTANT_Methodref_info{
    u2  access_flags;        方法的访问标志
    u2  name_index;          指向方法名的索引
    u2  descriptor_index;    指向方法类型的索引
    u2  attributes_count;    方法属性计数器
    attribute_info attributes;
}
字段访问标志 描述
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_VARARGS 0X0080 方法是否接收参数
ACC_NATIVE 0X0100 方法是否为native
ACC_ABSTRACT 0X4000 方法是否为abstract
12-2.add方法表内容.png 12-3.add方法字节码文件内容.png

8.属性表

属性表并没有一个固定的结构,各种不同的属性只要满足以下结构即可:

CONSTANT_Attribute_info{
    u2 name_index;
    u2 attribute_length length;
    u1[] info;
}

继续查看刚才add方法中属性表内容:

13-1.属性表内容.png 13-2.add方法属性表类型.png
13-3.add方法字节码Code属性表内容.png
总结
上一篇下一篇

猜你喜欢

热点阅读