Class文件结构介绍[属性表集合]

属性表

  在前面的内容中属性表(attribute_info)已经出现多多次了,在Class文件、字段表、方法表中都可以携带自己的属性集合,用于描述某些场景专有的信息
在这里插入图片描述

  与class文件中其他的数据项目要求严格的顺序、长度和内容不同,属性表集合的限制稍微宽松些,不在要求各个属性表具有严格顺序,并且只要不与已有属性名称重复,任何人实现的编译器都可以向属性表中写入自己的属性信息,java虚拟机会忽略掉它不认识的属性,在最新的《Java虚拟机规范(JavaSE7)》版本中,预定义属性有21项,

属性表的总体结构:
在这里插入图片描述

名称类型属性
u2attribute_name_index1
u4attribute_length1
u1infoattribute_length

  attribute_info又可细分为以下21种(即《Java虚拟机规范(Java SE 7)》中预定义了的21项虚拟机实现应当能识别的属性):

属性名称使用位置含义
Code方法表中Java代码编译成的字节码指令(即:具体的方法逻辑字节码指令)
ConstantValue字段表中final关键字定义的常量值
Deprecated类中、方法表中、字段表中被声明为deprecated的方法和字段
Exceptions方法表中方法声明的异常
LocalVariableTableCode属性中方法的局部变量描述
LocalVariableTypeTable类中JDK1.5中新增的属性,它使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加
InnerClasses类中内部类列表
EnclosingMethod类中仅当一个类为局部类或者匿名类时,才能拥有这个属性,这个属性用于表示这个类所在的外围方法
LineNumberTableCode属性中Java源码的行号与字节码指令的对应关系
StackMapTableCode属性中JDK1.6中新增的属性,供新的类型检查验证器(Type Checker)检查和处理目标方法的局部变量和操作数栈所需要的类型是否匹配
Signature类中、方法表中、字段表中JDK1.5新增的属性,这个属性用于支持泛型情况下的方法签名,在Java语言中,任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)或参数类型(Parameterized Types),则Signature属性会为它记录泛型签名信息。由于Java的泛型采用擦除法实现,在为了避免类型信息被擦除后导致签名混乱,需要这个属性记录泛型中的相关信息
SourceFile类中记录源文件名称
SourceDebugExtension类中JDK1.6中新增的属性,SourceDebugExtension用于存储额外的调试信息。如在进行JSP文件调试时,无法通过Java堆栈来定位到JSP文件的行号,JSR-45规范为这些非Java语言编写,却需要编译成字节码运行在Java虚拟机汇中的程序提供了一个进行调试的标准机制,使用SourceDebugExtension就可以存储这些调试信息。
Synthetic类中、方法表中、字段表中标识方法或字段为编译器自动产生的
RuntimeVisibleAnnotations类中、方法表中、字段表中JDK1.5中新增的属性,为动态注解提供支持。RuntimeVisibleAnnotations属性,用于指明哪些注解是运行时(实际上运行时就是进行反射调用)可见的。
RuntimeInvisibleAnnotations类中、方法表中、字段表中JDK1.5中新增的属性,作用与RuntimeVisibleAnnotations相反用于指明哪些注解是运行时不可见的。
RuntimeVisible
ParameterAnnotations
方法表中JDK1.5中新增的属性,作用与RuntimeVisibleAnnotations类似,只不过作用对象为方法的参数。
RuntimeInvisible
ParameterAnnotations
方法表中JDK1.5中新增的属性,作用与RuntimeInvisibleAnnotations类似,只不过作用对象为方法的参数。
AnnotationDefault方法表中JDK1.5中新增的属性,用于记录注解类元素的默认值
BootstrapMethods类中JDK1.7新增的属性,用于保存invokedynamic指令引用的引导方法限定符

Code属性

  java程序方法体重的代码经过Javac编译器处理后,最终变为字节码指令存储在Code属性内,Code属性出现在方法表的属性集合中(如下图),但并非所有的方法都必须存在这个属性,譬如接口或者抽象类中的方法就不存在Code属性,如果方法表有Code属性存在。
在这里插入图片描述
Code属性的结构图:
在这里插入图片描述

名称类型数量
attribute_name_indexu21
attribute_lengthu41
max_stacku21
max_localsu21
code_lengthu41
codeu1code_length
exception_table_lengthu21
exception_tableexception_infoexception_table_length
attribute_countu21
attributesattribute_infoattribute_count

attribute_name_index
  指向CONSTANT_Utf8_info型常量的索引,常量值固定为“Code”,它代表了属性的名称。
在这里插入图片描述
在这里插入图片描述
attribute_length:指示了属性值的长度。由于attribute_name_index与attribute_length一共占6个字节,所以属性值的长度又等于属性表总长度-6。

max_stack:操作数栈(Operand Stacks)允许深度的最大值。在方法执行的任意时刻,操作数栈都不会超过这个深度. 虚拟机的时候需要根据这个值来分配栈帧(Stack Frame)中的操作占深度。

max_locals:代表了局部变量表所需的存储空间。在这里,max_locals的单位是Slot槽。Slot是虚拟机为局部变量分配内存所使用的最小单位。一个Slot的空间大小为四字节,对于byte、char、float、int、 short、 boolean、和returnAddress等长度不超过32位的数据类型,每个局部变量占用一个Slot;而double和long这种64位的数据则需要两个连续的Slot来存储。
:并不是在方法中用到了多少局部变量,就把这些局部变量所占用放入Slot个数之和作为,max_locals的值,原因是局部变量表中的Slot槽可以重用。当代码执行超出一个局部变量的做用户与使时,这个局部变量所占用放入Slot可以被其它局部变量所使用,Javac编译器会根据变量的作用域来分配Slot给各个变量使用,然后计算出max_locals的大小。

code_length、code:用来存储Java源码编译后的字节码指令。code_length代表字节码长度;code是用于存储Java字节码指令的一系列字节流。

public void fun1(){
	int b = 20;
	int c = 30;
	int d = b+c+age;
	System.out.println(d);
}

对照虚拟机字节码指令表 我们来看下Code中的指令

在这里插入图片描述

指令字节码说明
10 14bipush 20将20推送至栈顶
3Cistore_1将栈顶20存入第二个本地变量(b)
10 1Ebipush 30将30推送至栈顶
3Distore_2将栈顶30型数值存入第三个本地变量( c )
1Biload_1将第二个int型本地变量推送至栈顶(b)
1Ciload_2将第三个int型本地变量推送至栈顶( c )
60iadd将栈顶两int型数值相加并将结果压入栈顶(b+c=50)
2Aaload_0将第一个引用类型本地变量推送至栈顶(age)
B4 00 13getfield #19获取指定类的实例域, 并将其压入栈顶age
60iadd将栈顶两int型数值相加并将结果压入栈顶(age+50=68)
3Eistore_3将第四个int型本地变量推送至栈顶(68)
B2 00 1Agetstatic #26获取指定类的静态域, 并将其压入栈顶
java/lang/System.out
1Diload_3将第四个int型本地变量推送至栈顶(68)
B6 00 20invokevirtual #32调用实例方法(调用println方法)
B1return从当前方法返回void

javap输出:
在这里插入图片描述

Exceptions属性

Exception属性的作用是列举出方法的声明异常。

名称类型数量
attribute_name_indexu21
attribute_lengthu41
number_of_exceptionsu21
exception_index_tableu2number_of_exceptions

exception_index_table是一个指向常量池中CONSTANT_Class_info型常量的索引,代表了异常的类型。

LineNumberTable属性

LineNumberTable属性用于描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系。
:LineNumberTable并不是必须的,javac编译时,可通过-g:none或-g:lines来取消或生成这项信息。

名称类型数量
attribute_name_indexu21
attribute_lengthu41
line_number_table_lengthu21
line_number_tableline_number_infoline_number_table_length

在这里插入图片描述

LocalVariableTable属性

LocalVariableTable属性用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间关系。
注:LocalVariableTable属性不是必须的,在javac编译时,可通过-g:none或-h:vars来取消或关闭这项信息。如果没有生成这项信息,最大的影响就是当别人引用这个方法时,所有的参数名称都将失去,IDE将会使用诸如arg0、arg1之类的占位符来代替原有的参数名,这对程序没什么影响,但是会对代码编写带来较大不便,而且在调试期间无法根据参数名称从上下文中获取参数值。
在这里插入图片描述

名称类型数量
attribute_name_indexu21
attribute_lengthu41
local_variable_table_lengthu21
local_variable_tablelocal_variable_infolocal_variable_table_lengt

local_variable_info的结构

名称类型数量
start_pcu21
lengthu21
name_indecu21
descriptor_indexu21
indexu21

在这里插入图片描述

  start_pc和length属性分别代表了这个局部变量的生命周期开始的字节码偏移量及其作用范围的覆盖长度。即:确定了这个局部变量的作用范围。

  nam_index和descriptor_index都是指向常量池中CONSTANT_Utf8_info型常量的索引,分别代表了局部变量的名称以及这个局部变量的描述符。

  index是这个局部变量在栈帧局部变量表中Slot的位置。如果这个局部变量是64位的,那么它占用的两个连续的Slot的位置是index和index+1。

  JDK1.5之后,新增了一个LocalVariableTable属性的“姐妹属性”—LocalVariablrTypeTablem,这个新增的属 性结构与LocalVariableTable非常相似,仅仅是把记录字段描述符的descripor_index替换成了字段的特征签名(Signature),对于非泛型类型来说,描述符和特征签名描述的信息基本是一致的,但是引入泛型后,由于描述符中泛型的参数类型被擦除掉,描述符就不能准确地描述泛型类型了,因此出现录入LocalVariableTypeTable。

SourceFile属性

SourceFile属性用于记录生成这个Class文件的源码文件名称

名称类型数量
attribute_name_indexu21
attribute_lengthu41
sourcefile_indexu21

sourcefile_index是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源码文件的文件名
在这里插入图片描述

ConstantValue属性

  ConstantValue属性的作用是通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量(即:类变量)才可以使用这项属性。对于类中的实例变量(即:非静态变量),赋值操作是在实例构造器中进行的。对于类变量(即:静态变量),有两种赋值方式可以选择:一种是在类构造器方法中进行赋值;另一种是使用ConstantValue属性进行赋值。

:目前Sun Javac编译器的选择是:如果这个变量的数据类型分是基本类型或者java.lang.String的话,就生成 ConstantValue属性来进行初始化,如果这个变量没有被final修饰,或者并非基本数据类型及字符串,则将会选择在方法(即:类构造器)中进行初始化。

名称类型数量
attribute_name_indexu21
attribute_lengthu41
constantvalue_indexu21

InnerClass属性

InnerClass属性用于记录内部类与宿主类之间的关联

名称类型数量
attribute_name_indexu21
attribute_lengthu41
number_of_classu21
inner_classesinner_classes_infonumber_of_class

number_of_classes代表记录了多少个内部类信息,每一个内部类信息都由一个inner_classes_info表进行描述。inner_classes_info结构为:

名称类型数量
inner_class_info_indexu21
outer_class_info_indexu21
inner_name_indexu21
inner_class_access_flagsu21

参考《深入理解Java虚拟机》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

波波烤鸭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值