JVM学习笔记之class文件结构【七】

2021-03-20  本文已影响0人  JiaJianHuang

一、概念

1.1 无符号数:

以 u1、u2、u3、u4、u8 代表 1 个字节,2 个字节、4 个字节、8 个字节的无符号数。无符号数可以描述数字,索引引用、数量值和按照 UTF-8 编码构成的字符串值。

1.2 表

1.3 class 文件组成

ClassFile {
    u4             magic;  //魔数, 用于识别class文件格式
    u2             minor_version;//次版本号
    u2             major_version;//主版本号
    u2             constant_pool_count;  //常量池计数器
    cp_info        constant_pool[constant_pool_count-1]; //常量池
    u2             access_flags;//访问标志
    u2             this_class;//类索引
    u2             super_class;//父类索引
    u2             interfaces_count;//接口计数器
    u2             interfaces[interfaces_count];//接口索引集合
    u2             fields_count;//字段计数器
    field_info     fields[fields_count];//字段表集合
    u2             methods_count;//方法计数器
    method_info    methods[methods_count];//方法表
    u2             attributes_count; //属性计数器
    attribute_info attributes[attributes_count];附加属性表
}

1.4 魔数

每个 Class 文件的头 4 个字节被称为魔数(Magic Number),它的唯一作用是确定这个文件是否能被虚拟机接受的 Class 文件。它的值是 0xCAFEBABE (咖啡宝贝),非常容易记忆。

1.5 版本号

紧接着的字节是次版本号(minor_version)和主版本号(major_version),Java 的版本号从 45 开始,Java1.1 之后的 JDK 大版本发布主版本号向上加一(Java1.0~Java1.1 使用了 45.0~45.3 的版本号)。注意高版本的 JDK 能向下兼容 以前的 Class 文件,但不能运行以后版本的 Class 文件。

1.6 常量池

常量池可以理解为 Class 文件的资源仓库,

主要存放:

1.7 访问标识(access_flags)

用于识别类和接口层次的访问信息

Flag Name Value Interpretation
ACC_PUBLIC 0x0001 是否为被声明为 public ,可以被其他外部包中访问
ACC_FINAL 0x0010 是否被声明 final,不能派生子类
ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the invokespecial instruction.
ACC_INTERFACE 0x0200 标识一个接口
ACC_ABSTRACT 0x0400 声明 abstract,抽象类,不能实例化
ACC_SYNTHETIC 0x1000 声明 synthetic; 标识这个类并非有用户代码产生
ACC_ANNOTATION 0x2000 标识这个一个注解
ACC_ENUM 0x4000 标识这是一个枚举

1.8 类索引、父类索引和接口索引

Class 文件就是由这三项数据来确定这个类的继承关系。类索引用于确定类的全限定类名,父索引用于确定父类的全限定类名,接口索引集合用于描述类实现了那些接口。

1.9 字段表集合

字段表集合[field_info] 用于描述接口或者类中声明的变量。字段(field) 包括类变量和实例变量,但不包括方法内部声明的局部变量。

描述符来描述方法时,按照先参数列表,后返回值的顺序描述;如:java.lang.String.toString() 描述为 () Ljava/lang/String,java.lang.String#valueOf(char[], int, int) 描述为 ([CII)Ljava/lang/String

1.10 方法表集合

方法描述采取与字段描述完全一致的方式。

1.11 属性表集合在

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

二、字节码指令

2.1 加载和存储指令

加载和存储指令用于将数据在栈帧中的局部变量表与操作数栈之间传输。

2.2 运算指令

相关指令
注意
double a = 1;
double b = a / 0;  //不会报错,结果Infinity

double a = 0.0;
double b = a / 0.0; //不会报错,结果NaN

2.3 类型转换指令

类型转换指令可以将两种不同的数值类型进行互相转换,一般用于用户代码中的显示类型转换操作,隐式类型转换不同转换指令,虚拟机直接支持。

2.4 对象创建与访问指令

2.5 操作数栈的管理指令

2.6 控制转移指令

2.7 方法调用和返回指令

2.8 异常处理指令

athrow  显示抛出异常

2.9 同步指令

Java 虚拟机可以支持方法级别的同步和方法内部一段指令序列的同步,这两种同步结构都使用管理(Monitor)来支持。

三、例子解析

public class DemoDynamic {
    public static void foo() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 5;
    }
}

四、字节码增强

具体详情看 字节码增强技术探索,这里只简单列出相关工具及使用场景。

12e1964581f38f04488dfc6d2f84f003110966

4.1 ASM

对于需要手动操纵字节码的需求,可以使用 ASM,它可以直接生产 .class 字节码文件,也可以在类被加载入 JVM 之前动态修改类行为

3c40b90c6d92499ad4c708162095fe3029983

4.2   Javassist

利用 Javassist 实现字节码增强时,可以无须关注字节码刻板的结构,其优点就在于编程简单。直接使用 java 编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构或者动态生成类。

4.3 Instrument

instrument 是 JVM 提供的一个可以修改已加载类的类库,专门为 Java 语言编写的插桩服务提供支持。它需要依赖 JVMTI 的 Attach API 机制实现。注意:ASM 和 Javassist 操作字节码库只能在类加载前对类进行强化。

4.5 字节码增强技术使用场景

参考

上一篇下一篇

猜你喜欢

热点阅读