前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Class文件结构介绍[属性表集合]

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

作者头像
用户4919348
发布2019-04-02 10:57:46
1.7K0
发布2019-04-02 10:57:46
举报
文章被收录于专栏:波波烤鸭波波烤鸭

属性表

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

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

属性表的总体结构:

名称

类型

属性

u2

attribute_name_index

1

u4

attribute_length

1

u1

info

attribute_length

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

属性名称

使用位置

含义

Code

方法表中

Java代码编译成的字节码指令(即:具体的方法逻辑字节码指令)

ConstantValue

字段表中

final关键字定义的常量值

Deprecated

类中、方法表中、字段表中

被声明为deprecated的方法和字段

Exceptions

方法表中

方法声明的异常

LocalVariableTable

Code属性中

方法的局部变量描述

LocalVariableTypeTable

类中

JDK1.5中新增的属性,它使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加

InnerClasses

类中

内部类列表

EnclosingMethod

类中

仅当一个类为局部类或者匿名类时,才能拥有这个属性,这个属性用于表示这个类所在的外围方法

LineNumberTable

Code属性中

Java源码的行号与字节码指令的对应关系

StackMapTable

Code属性中

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相反用于指明哪些注解是运行时不可见的。

RuntimeVisibleParameterAnnotations

方法表中

JDK1.5中新增的属性,作用与RuntimeVisibleAnnotations类似,只不过作用对象为方法的参数。

RuntimeInvisibleParameterAnnotations

方法表中

JDK1.5中新增的属性,作用与RuntimeInvisibleAnnotations类似,只不过作用对象为方法的参数。

AnnotationDefault

方法表中

JDK1.5中新增的属性,用于记录注解类元素的默认值

BootstrapMethods

类中

JDK1.7新增的属性,用于保存invokedynamic指令引用的引导方法限定符

Code属性

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

Code属性的结构图:

名称

类型

数量

attribute_name_index

u2

1

attribute_length

u4

1

max_stack

u2

1

max_locals

u2

1

code_length

u4

1

code

u1

code_length

exception_table_length

u2

1

exception_table

exception_info

exception_table_length

attribute_count

u2

1

attributes

attribute_info

attribute_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 14

bipush 20

将20推送至栈顶

3C

istore_1

将栈顶20存入第二个本地变量(b)

10 1E

bipush 30

将30推送至栈顶

3D

istore_2

将栈顶30型数值存入第三个本地变量( c )

1B

iload_1

将第二个int型本地变量推送至栈顶(b)

1C

iload_2

将第三个int型本地变量推送至栈顶( c )

60

iadd

将栈顶两int型数值相加并将结果压入栈顶(b+c=50)

2A

aload_0

将第一个引用类型本地变量推送至栈顶(age)

B4 00 13

getfield #19

获取指定类的实例域, 并将其压入栈顶age

60

iadd

将栈顶两int型数值相加并将结果压入栈顶(age+50=68)

3E

istore_3

将第四个int型本地变量推送至栈顶(68)

B2 00 1A

getstatic #26

获取指定类的静态域, 并将其压入栈顶java/lang/System.out

1D

iload_3

将第四个int型本地变量推送至栈顶(68)

B6 00 20

invokevirtual #32

调用实例方法(调用println方法)

B1

return

从当前方法返回void

javap输出:

Exceptions属性

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

名称

类型

数量

attribute_name_index

u2

1

attribute_length

u4

1

number_of_exceptions

u2

1

exception_index_table

u2

number_of_exceptions

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

LineNumberTable属性

LineNumberTable属性用于描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系。

:LineNumberTable并不是必须的,javac编译时,可通过-g:none或-g:lines来取消或生成这项信息。

名称

类型

数量

attribute_name_index

u2

1

attribute_length

u4

1

line_number_table_length

u2

1

line_number_table

line_number_info

line_number_table_length

LocalVariableTable属性

LocalVariableTable属性用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间关系。

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

名称

类型

数量

attribute_name_index

u2

1

attribute_length

u4

1

local_variable_table_length

u2

1

local_variable_table

local_variable_info

local_variable_table_lengt

local_variable_info的结构

名称

类型

数量

start_pc

u2

1

length

u2

1

name_indec

u2

1

descriptor_index

u2

1

index

u2

1

  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_index

u2

1

attribute_length

u4

1

sourcefile_index

u2

1

sourcefile_index是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源码文件的文件名

ConstantValue属性

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

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

名称

类型

数量

attribute_name_index

u2

1

attribute_length

u4

1

constantvalue_index

u2

1

InnerClass属性

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

名称

类型

数量

attribute_name_index

u2

1

attribute_length

u4

1

number_of_class

u2

1

inner_classes

inner_classes_info

number_of_class

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

名称

类型

数量

inner_class_info_index

u2

1

outer_class_info_index

u2

1

inner_name_index

u2

1

inner_class_access_flags

u2

1

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年03月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 属性表
    • Code属性
      • Exceptions属性
        • LineNumberTable属性
          • LocalVariableTable属性
            • SourceFile属性
              • ConstantValue属性
                • InnerClass属性
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档