classfile解析

前言

在讲解内容之前我们要先了解什么是classfile:在java中,通过javac编译生成class文件,这个Class文件是一组以8个字节为基础单位的二进制字节流。各项数据会严格的按照顺序紧凑的排列在class文件中,中间没有分隔符,使得class文件存储的内容几乎全部都是程序运行的。

可以使用Sublime_Text,IDEA 中的插件 BinEd - Binary/HexadecimalEditor来查看classfile

 

Classfile文件结构解析

class文件格式采用的类似C语言的结构体的伪结构来存储。这种结构只有两种数据类型: 无符号数

1. 无符号数

属于基本数据类型 主要用于描述数字索引符号数量值或者按照UTF-8编码构成的字符串值。而数据类型包括:u1(1个字节)、u2(2个字节)、u4(4个字节)、u8(8个字节)。

2.表

由多个无符号数或者其他表作为数据项构成的复合数据类型。所有的表都习惯以_info结尾 表主要用于描述有层次关系的复合结构数据。 比如 方法字段需要注意的是class文件没有分隔符,所以每个二进制数据类型都是严格定义的,具体的顺序如下:

 3.魔数(magicNumber

  • 每一个class文件的头4个字节 被称为魔数 magicNumbe

        从上表可以看到magic类型u4因此占用4个字节,他可以判断该文件的类型,如java的class文件的魔数值为0xCAFEBABE,而不是单纯的以后缀名判断文件类型。因为在某些情况下文件的后缀是可以改变的,而他的魔数值是不可改变的。

  • 唯一做你哥是用于确定这个文件是否为一个能被虚拟机接受的class文件

4.class文件版本号

紧挨着魔数的4个字节表示class的文件的版本号 版本号:

        次版本号 --minor_version 前2个字节用于表示次版本号

例如:

 

        主版本号 --major_version 后2个字节用于表示主版本号

例如: 

 

而主版本号随着jdk版本的不同而表示不同版本的范围。Java的版本号是从45开始的

如果class的版本号超过虚拟机的版本 会被拒绝执行。

JDK1.2 ----0X002E   46

JDK1.3 ----0X002F   47

JDK1.4 ----0X0030   48

JDK1.5 ----0X0031   49

JDK1.6 ----0X0032   50

JDK1.7 ----0X0033   51

JDK1.8 ----0X0034   52

5.常量池(CONSTANT_POOL_COUNT和CONSTANT_POOL)

5.1 constant_pool_count(常量的个数)

规则:从1开始计数的,第0项腾出来满足后面某些指向常量池的索引值的数据,在特定的情况下需要表达“不引用任何一个常量池项目” 把索引值的第0项留给JVM自己用。

案例中

将其转换为十进制代表常量池的数量为32,因为我们从1开始计数因此有31个常量池

5.2 CONSTANT_POOL(常量池)

规则:没有索引值为0的入口的,但是在CONSTANT_POOL_COUNT缺失的第0项也是要被计算在内的。 

 CONSTANT_POOL是类型数据集合,在该常量池中,每一项常量都是一个表 共有14种,这14种结构的表都是不相同的结构数据。14个表都有一共同的特点,都是由u1的标志位开始的,可以通过这个标志位来判断这个常量属于哪种常量的类型。

 

 

常量池中主要存放两大类常量:

  1. 字面量: 比较接近java语言层面的常量的概念 比如 字符串 被final关键字声明的常量值。
  2. 符号引用: 属于编译原理方面的概念 包括三项。
  •         类和接口的全名
  •         字段的名称和描述符
  •         方法的名称和描述符

5.3 常量池详细解析常量类型

总共有18个编号的常量类型。

编号1: CONSTANT_UTF8_INFO

TAG1 ------占用一个空间字节

Length: utf-8字符串占用的字节数

Bytes 长度为length字符串

用于表示utf-8的编码的字符串

编号3 CONSTANT_integer_info

Tag3

Bytes 4个字节 Big_Endian(高位在前) 存储int类型的值

编号4 CONSTANT_float_info

Tag4

Bytes 4个字节 Big_Endian(高位在前) 存储float类型的值

编号5 CONSTANT_long_info

Tag5

Bytes 8个字节 Big_Endian(高位在前) 存储long类型的值

编号6 CONSTANT_double_info

Tag6

Bytes 8个字节 Big_Endian(高位在前) 存储double类型的值

编号7 CONSTANT_Class_info

Tag7

Index 2个字节 指向类的全限定名的项的索引

类和接口符号引用

编号8 CONSTANT_String_info

Tag8

Index 2个字节 指向字符串的字面量的索引

编号9 CONSTANT_Fieldref_info

Tag9

Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项

Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项

字段的符号引用

编号10 CONSTANT_Methodref_info

Tag10

Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项

Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项

类中方法的符号引用

编号11 CONSTANT_InterfaceMethodref_info

Tag11

Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项

Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项

接口中方法的符号引用

编号12 CONSTANT_NameAndType

Tag12

Index 2个字节 指向该字段或方法名称常量项的索引
Index 2个字节 指向该字段或方法描述符常量项的索引

字段或方法的符号引用

编号15 CONSTANT_MethodHandler_info

Tag15

Reference_kind 1个字节 1-9之间的一个值 决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为

Reference_index 2个字节 对常量池的有效索引。

表示方法句柄

编号16 CONSTANT_MethodType_info

Tag16

Descriptor_index 2个字节 指向UTF8_info 结构表示的方法描述符

编号18CONSTANT_InvokeDynamic_info

Tag18

Bootstrap_method_attr_index: 2个字节 当前class文件中引导方法表的bootstrap_methods[] 数组的有效索引

Name_and_type_index: 2个字节 指向NameAndType_info 表示方法名和方法描述符。

表示动态方法的调用点。

6.access_flag

用于表示对该类或接口的访问权限以及该类或接口的属性

7. this_class

该this_class 项目的值 必须是constant_pool表中的有效索引,该constant_pool索引处的条目必须是表示此文件定义的类或接口 CONSTANT_Class_info 结构class

8.super_class

必须是constant_pool表中的有效索引, 如果super_class的值不为0 则constant_pool中的条目必须为CONSTANT_Class_info 结构 这个结构表示此类的文件定义的类的直接超类。直接超类不能在其classfile结构的access_flag项中设置 ACC_FINAL 标志。 其实要描述的意思就是说 如果superclass指代的超类,那么它就不能被final修饰

9.Method

Method属性包含三个字段值

名称access_flag  类型u2 数量1个attributes_count  1

名称 name_index  类型u2 数量1个 attribute_info attributes ----attributes_count

名称 descriptior_index 类型u2  数量1个

descriptior_index

  • 参数列表(参数类型) 后-返回值
  • void m()  等同于 ()V
  • String toString()  ->()Ljava/lang/String;
  • Long pos(int[] arr1,int arr2,long length) ->([IIJ)J

[ 一维数组

[[ 表示二维数组

10.Fields

名称access_flag  类型u2 数量1个attributes_count  1

  同method属性

名称 name_index  类型u2 数量1个 attribute_info attributes ----attributes_count

名称 descriptior_index 类型u2  数量1个

  同method属性值。

11.attributes_count 和attribute

attributes_count 附加属性  方法中的附加属性就是code,那么code在这里是比较重要的概念,code是具体代码的实现,当我们写入方法的时候,它能够把方法中代码转化为一条条指令。

在官方文档中,可以看到更多十六进制的数字,那么它们对应方法中code的内容实现。

Attributes附加属性

附加属性中 有的代码中存在内容,有的不存在内容

  • 既有预定义的属性,也可以自定义 java虚拟机会自动忽略它不认识属性
  • Code 表示的是方法表 方法表能够编译成字节码指令,还存放了操作数栈和局部变量的信息。

 

 

u2 attribute_name_index  指向常量池中的CONSTANT_UTF8_info 存放的当前属性的名字就是code。

u4 attribute_length 表示的code属性的长度 (不包括前6个字节)。

u2 max_stack 指定当前方法被执行引擎执行的时候,在栈帧中需要分配的操作数栈的大小

u2 max_locals 指定当前方法被执行引擎执行的时候,在栈帧中需要分配的局部变量表的大小

u4 code_length 指定方法字节码的长度, class文件中每条字节码都占用一个字节

u1 code 存放字节码指令本身,它的长度是code_length个字节。

U2 exception_table_length指定异常表的大小

Exception_table异常表 作用对try-catch-finally的描述,可以把它看成是一个数组。每一个数组项都是一个exception_info结构, 一般来说每个catch块对应一个exception_info,编译器也可能会对当前的方法生成一些exception_info.

 

U2 start_pc 是从字节码code属性中的一部分 起始处到当前异常处理器的起始处的偏移量量

u2 end_pc 从字节码起始处到当前异常处理器 末尾的偏移量

u2 handler_pc 是指当前异常处理器用于处理异常(即catch块)的第一条指令相对于字节码开始处的偏移量。

U2 catch_type 是常量池的索引 指向的是常量池CONSTANT-Class_info 数据项,描述了catch块中的异常类型的信息。这个类必须是java.lang.Throwable的或者是它的子类。

总结:

 如果偏移量从start_pc到end_pc之间,如果字节码出现了catch_type所描述的异常,那么就跳转偏移量到handler_pc的字节码中去执行。如果catch_type 为0 就代表不引用任何常量池的信息,那么这个exception_info 用于实现finally的子句。

U2 attribute_count 表示的是code属性中存在的其他属性的个数。会出现在 class中的属性,在field属性也有,在method属性也有

ConstantValue ----字段表 final关键字自定义的常量值

Deprecated  ---类 方法表 字段表

Exception 异常表

EnclosingMethod 类文件 局部类或匿名类的外部封装方法

InnerClass 类文件 内部类列表

可选属性

LineNumberTable  源码的行号和字节码行号的对应关系 可以把这个属性看成是一个数组,

数组中的每项LineNumberinfo结构描述了一条字节码和源码行号的对应关系

LocalVariableTable  建立了方法中的局部变量与源代码中的局部变量的对应关系。


12. 案例分析

 

常量池第一行:

        0a00:0a转换成十进制为10,这里的10对应常量池详细解析常量类型

 

则为 CONSTANT_Methodref_info;

0005表示 index 2个字节,指向声明方法的类或接口的描述符#5 CONSTANT_Class_info的索引项。指向#5行的内容。

0013表示 index 2个字节,指向#19的内容,其指向字段描述符CONSTANT_NameAndType_info的索引项


其他相关的类似 

常量池中第六行

   01 对应CONSTANT_UTF8_INFO

 

0006 表示占用的字节数和长度

3c69 6e69 743e 表示字符串内容的字节。init字符串的构造方法

这段总共是9个字节

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值