类文件结构——深入理解Java虚拟机

类文件结构

一、平台无关性的基础

  1. 各种不同平台的虚拟机与所有平台统一使用字节码(ByteCode)作为程序的存储格式,是构成平台无关性的基础。
  2. 实现语言无关性的基础也是虚拟机和字节码存储格式
    语言无关性

二、Class类文件结构

(注: 任何一个Class文件都对应唯一一个类或者接口的定义信息,但类和接口并不一定都跟都定义在文件里,如可以通过类加载器直接生成。)

(1) Class文件简介
  1. Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的安排在Class文件之中,中间无任何分隔符。当遇到需要占用8位字节以上空间的数据项时,按照高位在前的方式分割成若干个8位字节进行存储。
  2. Class文件数据类型: 无符号数和表
    1) 无符号数
    u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节。无符号数描述数字、索引引用、数字量或者按照UTF-8编码构成的字符串值。
    2) 表
    由多个无符号数或者其他表作为数据项构成的复合数据类型。以_info结尾。

  3. Class文件格式
    Class文件结构

(2) Class文件详解

这里写图片描述
1. 魔数(Magic Number)
用以确定这个文件是否是一个能被虚拟机接受的Class文件。(4个字节0xCAFEBABE)
2. 次版本号和主版本号
第5、6字节存储的是次版本号(Minor Version),第7、8字节存储的是主版本号(Major Version)。高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件,即使文件格式并未发生变化,虚拟机也必须拒绝执行超过其版本好的Class文件。
3. 常量池

1)常量池Class文件中的资源仓库,是与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,还是Class文件中第一个出现表数据类型的数据项目。

2)在常量池入口处放置了一项u2类型的数据constant_pool_count,代表常量池容量计数值。该容量计数器从1而不是从0开始,如容量为0x0016,即十进制的22,这代表常量池中常量有21个,索引范围为1~21Class文件结构中只有常量池的容量计数是从1开始的,对于其他集合类型,包括接口索引集合、字段表集合、方法表集合等的容量计数都是从0开始。

3)常量池存放类型(常量都是表结构
- 字面量(字符串、 final常量)
- 符号引用(类和接口的全限定名、字段的名称与描述符、方法的名称与描述符)

4) 常量池常量类型(表结构)
常量池常量类型
表开始的第一位是一个u1类型的标志位(tag),代表当前这个常量属于哪种常量类型。可以使用javap命令输出常量表, 如javap -verbose TestClass

4. 访问标志(Access_flags, U2)
访问标志

5. 类索引、父类索引、接口索引
1) 类索引this_class(U2)确定这个类的全限定名,父类索引super_class(U2) 确定这个类的父类的全限定名。
2) 接口索引描述这个类实现的接口类型,按照顺序排列在接口索引集合中。

6. 字段表集合
字段表用于描述接口或者类中声明的变量,字段包括类级变量以及实例级变量,但不包括在方法内声明的局部变量。

u2 fields_count;   // 有多少个字段

field_info {  
    u2 access_flags;   // 例如是public , private 等等,字段访问标志
    u2 name_index;   // 指向常量池的入口,字段的简单名称
    u2 descriptor_index;   //指向常量池的入口,字段以及方法的描述符
    u2 attributes_count;   // 该字段的属性有多少个,ConstantValue
    attribute_info attributes[attributes_count];  //属性信息
}

字段的描述符:用来描述字段的数据类型、方法的参数列表(包括数量、顺序、以及类型)和返回值。
1) 基本数据类型(byte/char/double/float/int/long/short/boolean)以及代表无返回值void类型采用一个大写字母表示;对象类型用字符L加对象的全限定名表示,如Ljava/lang/Object
字段描述符

2) 对于数组,java.lang.String[][] —> [[Ljava/lang/String; int[] —> [I
3) 描述符描述方法时,按照先参数表,后返回值的顺序描述,参数列表严格按照参数顺序放在一个小括号内()。如void inc()描述符为()V; java.lang.String.toString()描述符为()Ljava/lang/String;; (复杂) int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)的描述符为([CII[CIII)I

7. 方法表集合

u2 methods_count;   // 有多少个方法

method_info {  
    u2 access_flags;   //访问标志
    u2 name_index;     //名称索引
    u2 descriptor_index;   //描述符
    u2 attributes_count;  //属性表集合,包括Code属性
    attribute_info attributes[attributes_count]; 
} 

8. 属性表集合
Class文件、字段表、方法表可以携带自己的属性表集合,用于描述某些场景专有的信息,不要求各个属性表具有严格的顺序。
(1) Code属性
Code属性表结构如下:
这里写图片描述

Code_attribute {  
  u2 attribute_name_index;  //指向常量池,应该是UTF8Info ,值为"Code"
  u4 attribute_length;      //属性长度, 不包括开始的6个字节
  u2 max_stack;             // 操作数栈的最大深度(注:编译时已经确定)
  u2 max_locals;            // 最大局部变量表个数(第一个Slot存放this对象引用)
  u4 code_length;           // 该方法的代码长度
  u1 code[code_length];     //真正的字节码
  u2 exception_table_length;    
  { 
        u2 start_pc;   
        u2 end_pc;   
        u2 handler_pc;   
        u2 catch_type;  
} exception_table[exception_table_length];  
  u2 attributes_count;  
  attribute_info attributes[attributes_count]; 
} 

(2) Exception属性
列举出方法中可能抛出的受检查异常Checked Exception,即throws关键字后面列举的常量。
(3) LineNumberTable属性
用于描述Java源码行号与字节码偏移量的对应关系。javac可以使用-g:none或者-g:lines选项来取消或者要求生成这项信息。
(4) LocalVariableTable属性
用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,也不是运行时比必须的属性。-g:none或者-g:vars选项可以取消或者要求生成这项信息。
(5) SourceFile属性
记录生成的这个Class文件的源码文件名称。
(6) ContantValue属性(通知虚拟机自动为静态变量赋值)

如果某字段为静态类型( access_flags 中包含 ACC_STATIC 标志),
将会被分配 ConstantValue 属性

(7) InnerClass.....

(3) 字节码指令(最多256个)

Java虚拟机的指令由一个字节长度的、代表某种特定操作含义的数字(操作码)以及跟随其后的零至多个代表此操作所需参数(操作数)构成。

  1. 大部分的指令中没有支持整数类型byte\char\short\boolean,编译器会在编译期或者运行期将byteshort类型的数据带符号扩展为相应的int类型,将booleanchar零位扩展为相应的int类型。

  2. 加载和存储指令
    用于将数据在栈帧中的局部变量表和操作数栈之间来回传输,如iloadistore 等。

  3. 运算指令
    用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作数栈作栈顶。如iadd/isub/dcmpg

  4. 类型转换指令

  5. 对象创建与访问指令
    1) 创建类实例: new
    2) 创建数组: newarray、anewarray、multianewarray
    3) 访问类字段和实例字段: getfield、putfield、getstatic、putfield

  6. 操作数栈管理指令
    出栈: pop、pop2
    入栈: dup、dup2…
    交换: swap

  7. 控制转移指令,有条件或者无条件的修改PC寄存器的值,如ifeq

  8. 方法调用和返回指令

  9. 异常处理指令

  10. 同步指令

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值