类文件结构

Class类文件的初步印象

Class文件采用一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有任何分割符。
Class文件格式采用一种类似于C语言结构体的伪结构来存储数据:无符号数和表数据类型。
①无符号数:基本数据类型,以u1、u2、u4、u8来分别代表1字节、2字节、4字节、8字节的无符号数,无符号数可以用来描述数字、索引引用、数量值、按照UTF-8编码构成字符值。
②表:由多个无符号数或者其他表组合而成的复合数据类型。表一般采用_info结尾。


类文件结构_第1张图片
1.jpg

本片分析的class文件:

package jvm_source;

public class HelloWorldJvm {

    private String str = "123";
    public int i;
    
    public String calculate() {
        i++;
        return str;
    }
    
}

魔数与Class文件的版本

什么是魔数?

每个class文件的头4个字节就叫做魔数。

魔数的作用?

魔数的唯一作用就是确定这个文件是否为一个能被jvm接受的class文件。


类文件结构_第2张图片
1.jpg
说明

jvm加载class文件不是采用后缀名.class加载,而是通过魔数来区分该class文件是否可以加载。

class文件的版本

第5和第6个字节是次版本号,第7和第8个字节是主版本号。


类文件结构_第3张图片
3.jpg
JDK 1.8 = 52
JDK 1.7 = 51
JDK 1.6 = 50
JDK 1.5 = 49
JDK 1.4 = 48

解释:图中的主版本号是33(16进制),那么对应10进制就是51。说明当前我的jdk版本是1.7的。

常量池

接下来的Class文件中就是常量池中常量数量(constant_pool_count)和常量表(constant_pool)。
常量池主要存放:
① 字面量:java语言层面的常量,如文本字符串,声明为final的常量值等。
② 符号引用:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。

常量池中常量数量
类文件结构_第4张图片
1.jpg

注意:常量容量计数不是从0开始的,而是从1开始的。从图中可以看出常量池中常量的数量是31个。

常量

常量计算规则表:


类文件结构_第5张图片
3.jpg
类文件结构_第6张图片
IMG_0174.JPG

类文件结构_第7张图片
IMG_0175.JPG

解释:基于上面我们分析当前的class文件中的常量。


类文件结构_第8张图片
4.jpg

第一个常量
tag : 07 :(对应类型:CONSTANT_Class_info)
index : 00 02 : (指向常量池中第2个常量)


第二个常量
tag:01:(对应类型:CONSTANT_Utf8_info)
length:00 18 :(UTF-8编码的字符串占用的字符数24)
bytes:(长度为length24的UTF-8编码的字符串。)如下
16进制:
6A 76 6D 5F 73 6F 75 72 63 65 2F 48 65 6C 6C 6F 57 6F 72 6C 64 4A 76 6D
ASCII码:
j v m _ s o u r c e / H e l l o W o r l d J v m


第三个常量
tag : 07 :(对应类型:CONSTANT_Class_info)
index : 00 04 : (指向常量池中第4个常量)


第四个常量
tag:01:(对应类型:CONSTANT_Utf8_info)
length:00 10:(UTF-8编码的字符串占用的字符数16)
bytes:(长度为length16的UTF-8编码的字符串)
16进制:
6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74
ASCII码:
j a v a / l a n g / O b j e c t


至于后面还有28个常量,按照上面的计算表格,可以依次往后分析。

javap -verbose class文件名命令
类文件结构_第9张图片
1.jpg

类文件结构_第10张图片
2.jpg

该命令可以查看常量池中31个常量等信息。

访问标志

常量池完事之后就是访问标志,占两个字节,这个标志用于标识一些类或者接口层次的访问信息。access_flag中一共有16个标志位可以使用。当前只定义了8个,没有使用到的标志位一律为0。

规则参照
类文件结构_第11张图片
6.jpg
实例分析
类文件结构_第12张图片
5.jpg

解释:
我们的class类中使用了ACC_PUBLIC和ACC_SUPER标志。注意:按位或运算在二进制中只要有一个为1,那么结果就是1。

0x0001|0x0020 = 0x0021

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

类索引、父类索引、接口索引集合都按照顺序排列在访问标志之后。

类索引

类索引用于确定这个类的全限定名;类索引是一个u2类型的数据。

父类索引

父类索引用于确定这个类的全限定名;父类索引是一个u2类型的数据。

接口索引集合

接口索引集合用来描述这个类实现了哪些接口(implements语句);接口索引集合是一组u2类型的数据的集合。

实例分析
类文件结构_第13张图片
7.jpg

类索引指向01,父类索引指向03,接口索引集合大小为00。
根据前面javap -verbose命令查看


类文件结构_第14张图片
8.jpg

字段表集合

字典表用于描述接口或者类中声明的变量。其中字段包括类级别变量和实例级变量。但是不包括在方法内部声明的局部变量。
字段表结构:


类文件结构_第15张图片
11.jpg

参照规则:


类文件结构_第16张图片
10.jpg
实例分析
类文件结构_第17张图片
12.jpg

解释:
00 02:一个u2类型的数据为容量计数器attributes_count
00 02:容量计数器的access_flag访问标志,参照访问标志规则为ACC_PRIVATE
00 05:字段名称的索引name_index,值为str


类文件结构_第18张图片
13.jpg

00 06:字段描述符索引desciptor_index,为String


15.jpg

方法表集合

方法表和字段表集合中的结构差不多。
方法表结构:


类文件结构_第19张图片
1.jpg

解释:
access_flags:访问标志,意思就是这个方法的访问标志是public的还是其他。
name_index:名称索引,指向常量池中方法的名称。
descriptor_index:描述符索引,意思就是指向常量池中方法的返回值类型。
attribute_count:此方法的属性表集合中有多少个属性。
attributes:属性表中指向属性名称的索引。(对应常量是Code)。


方法表访问标志:


类文件结构_第20张图片
2.jpg

实例分析:

package jvm_source;

public class HelloWorldJvm {

    private String str = "123";
    
    public String calculate() {
        return str;
    }
}

javac编译成class文件:


类文件结构_第21张图片
3.jpg

00 02:为u2类型的数据,意思是集合中有多少个方法。此处2个方法。
00 01:访问标志:查表可以看出01为ACC_PUBLIC。
00 08:名称索引。值为


类文件结构_第22张图片
4.jpg

00 09:描述符索引:
类文件结构_第23张图片
5.jpg

00 01:此方法的属性表集合中有1个属性
00 0A:属性表中指向属性名称的索引10。指向Code属性表。


类文件结构_第24张图片
指向

属性表集合

参照规则:

属性名称 / 使用位置 / 含义
Code / 方法表 / Java代码编译成的字节码指令
ConstantValue / 字段表 / final关键字定义的常量池
Deprecated / 类,方法,字段表 / 被声明为deprecated的方法和字段
Exceptions / 方法表 / 方法抛出的异常
EnclosingMethod / 类文件 / 仅当一个类为局部类或者匿名类是才能拥有这个属性,这个属性用于标识这个类所在的外围方法
InnerClass / 类文件 / 内部类列表
LineNumberTable / Code属性 / Java源码的行号与字节码指令的对应关系
LocalVariableTable / Code属性 / 方法的局部便狼描述
StackMapTable / Code属性 / JDK1.6中新增的属性,供新的类型检查检验器检查和处理目标方法的局部变量和操作数有所需要的类是否匹配
Signature / 类,方法表,字段表 / 用于支持泛型情况下的方法签名
SourceFile / 类文件 / 记录源文件名称
SourceDebugExtension / 类文件 / 用于存储额外的调试信息
Synthetic / 类,方法表,字段表 / 标志方法或字段为编译器自动生成的
LocalVariableTypeTable / 类 / 使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加
RuntimeVisibleAnnotations / 类,方法表,字段表 / 为动态注解提供支持
RuntimeInvisibleAnnotations / 表,方法表,字段表 / 用于指明哪些注解是运行时不可见的
RuntimeVisibleParameterAnnotation / 方法表 / 作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法
RuntimeInvisibleParameterAnnotation / 方法表 / 作用与RuntimeInvisibleAnnotations属性类似,作用对象哪个为方法参数
AnnotationDefault / 方法表 / 用于记录注解类元素的默认值
BootstrapMethods / 类文件 / 用于保存invokeddynamic指令引用的引导方式限定符


关键属性名称解析

①Code属性:如果把一个java程序中的信息分为代码(Code,方法体里面的java代码)和元数据(包括类、字段、方法定义以及其他信息部分)。那么Code属性就是用来描述代码。
Code属性表结构:


类文件结构_第25张图片
6.jpg

实例分析:


类文件结构_第26张图片
8.jpg

00 00 00 27:attribute_length属性值长度为39。
00 02:操作数栈max_stack为2。
00 01:局部变量表存储空间max_locals为1。

00 00 00 0B:code_length字节码指令的长度为11。
2A B7 00 01 2A 12 02 B5 00 03 B1:code:字节码指令,每个字节码指令的长度为u1。


至于剩下的其他属性结构,依次往下分析。


切记:不要大晚上困倦去读,容易读串行了。。。

你可能感兴趣的:(类文件结构)