一.class类文件结构
class文件是一组8位字节二进制流。
分为两种数据结构,无符号数和表。
无符号数: 用u1,u2,u4,u8代表1,2,4,8个字节。 用来描述数字、索引引用、数量值、或者utf-8字符串。
表:多个无符号数或者其它表构成,习惯以_info结尾。
表中各个数据项的含义
1.magic 一串固定的值标示这是java文件 ,版本,表示它所对应java版本
2.常量池
u2类型来标示有多少个常量,它是从1开始计数的。
常量池有以下两种类型, 字面量(string, 声明为final的常量值) , 符号引用
符号引用包含以下三种类型:
a.类和接口的权限定名
b.字段的名称和描述符
c.方法的名称和描述符
比如constant_class_info类型, 它有一个u1类型的tag,标示它是什么样的数据。name_index是一个索引值,指向常量池中的另外一条常量。
而指向的这一条常量,是一个constant_string_info,它由 tag length bytes组成,构成了一个字符串,用以标示class的名称。
class --> string --> "具体的类的名称"
3.访问标志
access_flags 标示这个class 是class还是interface ,是public还是private等等
4.类索引,父类索引,接口索引集合
类索引和父类索引,他们都是指向一个 constant_string_info 类型的常量,最终标示其类的全名。
接口索引集合第一项为 数量,后面跟着偏移地址。
5.字段表集合
包含 类级变量和 实例级变量,不包括方法内的变量。
access_flags:描述字段的访问类型 private public等miaoshufu
name_index、descriptor_index:分别是简单名称和描述符,都是对常量池的引用。
简单名称:是指没有类型和参数修饰的方法或者字段名称, 如 inc() 便是 inc。
描述符:描述其参数以及返回值 如 string[][] 为 [[java/lang/string int aaa(char[] s,int i,int j)描述符为([CII)I参数类型在前,返回值类型在后。
attributes_count:
attributes:
6.方法表集合
access_flags:public、private、synchronized等
name_index,descriptor_index:描述方法的参数和返回值
*方法内的代码被编译成了code
7.属性表集合
class文件,属性表,方法表都可以有自己的属性表集合。
有以下属性:
1.code 用于方法表
2.constantvalue 字段表
3.deprecated 类,方法表 ,字段表,被声明为 deprecated 的方法和字段
4.exceptions 方法表,抛出异常
5.EnclosingMethod 类文件,匿名类或者局部类
6.innerClasses 类文件 内部类
7.LineNumberTable code属性 行号与字节码的关系
。。。以下省略五百字
code属性:
attributle_name_index/attribute_length:指向constant_utf8_info,常量值固定为code ,length为长度,分别为u2 u4类型,一共六个字节。
max_stack,操作数栈最大深度,这样虚拟机在分配栈帧时,不会超过这个值。
max_locals,局部变量表所需要的存储空间,代表了局部变量所需要的存储空间。每个slot可以存储32位的变量,double和long需要占用两个slot,并非有多少个变量就需要杜少slot,因为变量过期,就可以重用。
code_length, 用来存储编译生成的字节码指令,每个指令是一个u1类型的单字节,取值为 0x00 - 0xff,最大长度不超过 65535.
code,最重要的属性,其中stack为第几个方法,locals为局部变量的数目,args_size为传入参数的数目。在非static方法内,所有的arg_size至少为1,因为要传入this,也因此,即使没有局部变量,locals的数目也会等于该对象的属性。
异常处理表,并非必须存在,有start_pc,end_pc来标示try 哪一段代码,handler_pc进行处理,catch_type指向一个CONSTANT_Class_info的索引。
Exceptions属性
Exceptions描述的是列举出来方法中可能抛出的受查异常。其中number_of_exceptions标示错误的数量,exception_index_talbe是一个数量和number_of_exception相同的table,每一项都指向常量池中的class_info实例。
LineNumberTable
通过一个start_pc和line_number来对应字节码行号和源码行号,主要用于debug。
LocalVariableTable属性
描述栈帧中局部变量表与源码中定义的变量之间的关系
local_variable_info代表一个栈帧与变量之间的关联,结构如下
start_pc、lenght 两个变量共同描述了字节偏移量和它的覆盖范围
name_index和descriptor_index 都指向常量池中UTF-8_info的索引,代表局部变量的名称和局部变量的描述符。
index是这个局部变量在栈帧局部变量表中Slot的位置
SourceFIle属性
记录生成这个class的源码文件名称
ConstantValue属性
通知虚拟机自动为静态变量赋值,被static修饰的变量(即类变量)才可以使用这项属性。
实例变量,在<init>中赋值,而类变量,在<cinit>中赋值。
而用final 和 static修饰,才可以用constantvalue来赋值。
它也拥有一个constantvalue_index,代表了常量池中一个字面量常量的引用,有可能是long float double string
innerClasses属性
记录内部类和宿主类之间的联系,如果类有内部类,则会生成innerClasses属性。
由 attribute_name_index attribute_lenght number_of_classes(一共有多少个匿名内部类) inner_classes类型为inner_classes_info个数与number_of_classes相当,描述内部类信息
inner_classes_info
inner_class_info_index和outer_class_info_index都指向了常量池中的CONSTANT_Class_info型常量的索引,表明了内部类和宿主类的符号引用。
inner_name_index指向常量池中UTF-8型常量的索引,如果为匿名内部类,则为0.
inner_class_access_flags是匿名内部类的访问标志。有 public private 等等。
Deprecated和Synthetic属性
Deprecated为@deprecated产生的,Synthetic为编译器添加,为编译器自动产生。
他们的attribute_length为0x00000000因为灭有任何属性值需要设置。
StackMapTable属性
包含多个栈映射帧,都代表了一个字节码偏移量,用于标示执行到该字节码是局部变量表和操作数栈的验证类型。
Signature属性
由于java的泛型编译后会被擦除掉,所以无法通过反射来获取真正的泛型,所以Signature用来实现此功能。
signature_index是一个对常量池的索引,表示类签名,方法类型,或字段类型。
BootstrapMethods属性
包含bootstrap_method方法,为引导方法
二.字节码指令
虚拟机指令可以分为以下九种
1.加载和存储指令
将一个局部变量加载到操作栈 将数值从操作数栈存储到局部变量表 将常量加载到操作数栈 扩产个局部变量表的访问索引
2.运算指令 加减乘除 按位与或取反等
3.类型转换指令 int->long float double long->float double flat->double
4.对象创建与访问指令 创建对象new 创建数组newarray 访问类字段
5.操作数栈管理 pop push等操作
6.控制转移指令 跳转执行 如if等
7.方法调用和返回指令
8.异常处理
9.同步指令