虚拟机平台无关性(二)

字段表集合

字段表 (field_info) 用于描述接口或类中申明的变量. 字段(field) 包括类级别变量以及实例级别变量,但不包括在方法内部申明的局部变量.
在 Java 中描述一个字段包含哪些信息?1.作用域(private/public)、实例变量还是类变量(static)、可变性(final)、并发可见性(volatile)
字段数据类型(基本类型、对象、数组)、字段名称.

字段表结构
u2 access_flags
u2 name_index 简单名
u2 description_index 复杂名(带类型描述等信息)

下面两个用于存储一些额外的信息:
u2 attributes_count
attribute_info attribute

name_index 和 description_index 都是对常量池的引用,分别代表着字段的简单名称以及字段和方法的描述符.
com.hanlin.fadp.TestClass 是类的全限定名
简单名称是指没有类型和参数修饰的方法或字段名.

例如:()Ljava/lang/String; -> java.lang.String toString();

如果是 private static int a = 123;
那么 attributes_count = 1, attribute_info 可能存在一项名称为 ConstantValue 的属性,其值指向常量 123.

B
byte
J
long
C
char
S
short
D
double
Z
boolean
F
float
V
void
I
int
L
对象类型
注:字段表中不会列出从超类或者父接口中继承而来的字段,但是可能列出原本 Java 代码中不存在的代码. 例如:内部类为了保持对外部类的访问,会自动添加指向外部类实例的字段.

方法表集合

方法表的结构如同字段表一样,依次包括了访问标志 access_flags、名称索引 name_index、描述符索引 descriptor_index、属性表集合
attributes 几项.

u2 access_flags 1
u2 name_index 1 指向简单名
u2 descriptor_index 1 指向复杂名
u2 attributes_count 1 额外的属性长度
attribute_info attributes 额外的属性

因为 volatile 关键字和 transient 不能修饰方法,所以方法表的访问标志中没有了 ACC_VOLATILE 标志和 ACC_TRANSIENT 标志.

与之前相对的,synchronized、native、strictfp 和 abstract 关键字可以修饰方法,所以方法表的访问标志中增加了 ACC_SYNCHRONIZED、
ACC_NATIVE 等.

或许会有读者会有疑问,方法的定义可以通过访问标志、名称索引、描述符索引表达清楚,但方法里面的代码去哪了?方法里的 Java 代码,经过
编译器编译成字节码指令后,存放在方法属性表集合中一个名为 Code 的属性里面.

与字段表集合相对应的,如果父类方法在子类中没有被重写,方法表集合中就不会出现来自父类的方法信息. 但同样的,有可能会出现由编译器自动
添加的方法,最典型的便是类构造器 方法和实例构造器 方法.

在 Java 语言中,要重载一个方法,除看要与原方法具有相同的简单名称之外,还要求必须拥有一个与原方法不同的特征签名,特征签名就是一个方法
中各个参数在常量池中的字段符号引用的集合,也就是因为返回值不会包含在特征签名中,因此 Java 语言里面是无法仅仅依靠返回值的不同来
对一个已有方法进行重载的. 但是在 Class 文件格式中,特征签名的范围更大一些,只要描述符不是完全一致的两个方法也可以共存.
也就是说,如果两个方法有相同名称和特征签名,但返回值不同,那么也是可以合法共存于同一个 Class 文件中.

属性表集合(attribute_info)

JVM 对属性表集合的限制稍微宽松,不再要求各个属性表具有严格顺序,并且只要不与已有属性名重复,任何人实现的编译器都可以向属性表中写入
自己定义的属性信息,Java 虚拟机运行时会忽略掉它不认识的属性.

属性名称 使用位置 含义
Code 方法表 Java 代码编译成的字节码指令
ConstantValue 字段表 final 关键字定义的常量值
Deprecated 类、方法表、字段表 被少女革命为 deprecated 的方法和字段
Exceptions 方法表 方法抛出的异常
EnclosingMethod 类文件 仅当一个类为局部类或者匿名类时才能拥有这个属性,这个属性用于表示这个类所在的外围方法.
InnerCLasses 类文件 内部类列表
LineNumberTable Code属性 Java源码的行号与字节码指令的对应关系
LocalVariableTable Code属性 方法的局部变量描述

对于每个属性,它的名称需要从常量池中引用一个 Constant_Utf8_info 类型的常量来表示,而属性值的结构则是完全自定义的,只需要通过一个
u4 的长度属性去说明属性值所占用的位数集合(属性表是最宽松的,用户可以自定义!!!).

u2 attribute_name_index 1
u4 attribute_length 1
u1 info attribute_length

Code 属性

Java 程序方法体中的代码经过 Javac 编译器处理后,最终变为字节码指令存储在 Code 属性内. Code 属性出现在方法表的属性集合中,但并非
所有的方法表都必须存在这个属性,譬如接口或者抽象类中的方法就不存在 Code 属性. 如果方法表有 Code 属性存在,那么它的结构是如下:

u2 attribute_name_index
u4 attribute_length
u2 max_stack
u2 max_locals
u4 code_length
u1 code
u2 exception_table_length
exception_info exception_table
u2 attributes_count

attribute_name_index 是一项指向 Constant_Utf8_info 型常量的索引,常量值固定为 Code, 它代表了该属性的属性名称,
attribute_length 指示了属性值的长度,由于属性名称索引与属性值的长度一共 6 字节,所以属性值的长度固定为整个属性表长度减去 6 字节.
max_stack 代表了操作数栈深度的最大值. 虚拟机运行时需要根据这个值来分配栈帧中的操作栈深度.
max_locals 代表了局部变量表所需要的存储空间. max_locals 的单位是 Slot, Slot 是虚拟机为局部变量分配内存所使用的最小单位.
4 字节的数据占一个 Slot,超过四字节的占两个 Slot. 方法参数(包括实例方法中的隐藏参数 this)、显示异常处理器参数,就是 try-catch
语句中 catch 块所定义的异常、方法体中定义的局部变量都需要使用局部变量表来存放. 并不能把方法中用到了多少个局部变量所占用的 Slot 之和
作为 max_locals 的值,原因是局部变量表中的 Slot 可以重用,当代码执行超过一个局部变量的作用域时,这个局部变量所占用的 Slot 可以
被其他局部变量所使用.

code_length 和 code 用来存储 Java 源程序编译后生成的字节码指令. code_length 代表字节码长度,code 是用于存储字节码指令的一系列
字节流.

虚拟机规范中明确限制了一个方法不允许超过 65535 条字节码指令,即它实际只使用了 u2 的长度,如果超过这个限制,javac 编译器也会拒绝
编译.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值