Class文件结构介绍[属性表集合]
属性表
在前面的内容中属性表(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相反用于指明哪些注解是运行时不可见的。 |
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_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虚拟机》
Class文件结构介绍[属性表集合]相关推荐
- JVM虚拟机-Class文件之属性表集合
一.概述 在class文件中,属性表集合包括Java虚拟机预先规范定义的属性以及用户自定义的属性,对于用户自定义的属性,虚拟机加载的时候会自动忽略掉.class文件.字段表.方法表都可以携带自己的属性 ...
- jvm探秘五:Class类文件结构之属性表
概述 在Class文件.字段表和方法表都可以携带自己的属性信息,这个信息用属性表进行描述,用于描述某些场景专有的信息. 与Class文件中其它数据项对长度.顺序.格式的严格要求不同,属性表集合不要求其 ...
- 使用 Visual Studio 的属性表快速引用第三方的库
1 项目配置 1.1 传统配置方式 自从我开始使用 Visual C++ 系列开发工具开始,配置项目头文件目录,库文件目录几乎形成了肌肉记忆.由于 C/C++ 语言的特点,这些目录的位置如果设置不当, ...
- JVM-class文件完全解析-方法表集合
方法表集合 前面的魔数,次版本号,主板本号,常量池入口,常量池,访问标志,类索引,父类索引,接口索引集合,字段表集合,那么再接下来就是方法表了. 方法表的构造如同字段表一样,依次包括了访问标志(a ...
- JVM虚拟机-Class文件之方法表集合
一.概述 方法表集合与属性表集合的结构类似,是对方法的修饰符.返回类型.方法名.参数个数.参数类型.方法体的描述集合. 方法表集合的结构是一个类似于数组的结构,JVM在对java文件进行编译时,会将类 ...
- class属性表种类集合,以及字段详解
目录: java虚拟机汇总 class文件结构分析 1).class文件常量池中的常量项结构 2). 常用的属性表的集合<<== 现在位置 类加载过程 1).类加载器的原理以及实现 虚拟机 ...
- vs如何将工程配置,保存到属性表
本机有个: opencv412_release_x64_vs2017.props 现在介绍一种将工程配置,保存到属性表的方法,那么下次新建工程时,只要添加这个属性表,整个配置就完成了-- 首先新建一个 ...
- LINUX下的文件结构介绍
LINUX下的文件结构介绍 来源:互联网作者:佚名时间:04-15 17:37:28[大 中 小] 了解LINUX下的文件结构,方便学习linux,linux的文件比较特殊,刚开始学习确实不太习惯. ...
- C# 获取文件大小,创建时间,文件信息,FileInfo类的属性表
OpenFileDialog openFileDialog1 = new OpenFileDialog(); if(openFileDialog1.ShowDialog() == DialogResu ...
最新文章
- ceph rbdmap遇到的一个问题
- 网络知识:电脑无线网连接不上问题汇总!
- java完全解耦_java-完全解耦 - osc_bc7dotjc的个人空间 - OSCHINA - 中文开源技术交流社区...
- ffmpeg 转换flv压缩大小_使用ffmpeg进行视频文件转换成FLV整理
- linux下如何分区格式化硬盘,Linux之磁盘如何分区,格式化挂载
- 算法:Valid Parentheses(有效的括号)
- (戴尔灵越7572)笔记本外扩显示器以后,笔记本没有声音了的解决办法
- WinRAR 4.00 beta1 简体中文版
- ​怎么判断是前端bug还是后端bug?
- seo网站优化技巧_新网站的10个SEO技巧
- Python做一个“盯盘机器人”,实时监控股票价格并通知你!
- Unity 2D骨骼动画2:创建真实动画
- 宠物小精灵 动态数组题目解析
- 域名授权验证系统v1.0.6开源版本网站源码
- Git:不同仓库之间的cherry-pick
- 【Proteus仿真】键盘矩阵扫描+LCD128x64显示
- 数组的方法-push(),pop(),unshift(),shift()
- @Column注解解析
- vue中textarea监听粘贴事件获取图片
- 笔记:YUV444、YUV422、YUV420、YU12、YV12、NV12、NV21的区别
热门文章
- readme.md编写并生成html
- 希望我讲明白了G1 GC的过程
- IOS 开发中相机获取图片 不同方向的相机获取不同的图片的实现 ||图片的从新绘图
- R语言使用psych包的fa函数对指定数据集进行因子分析(输入数据为相关性矩阵)、使用rotate参数指定进行斜交旋转提取因子、编写自定义函数通过因子模式矩阵与因子相关性矩阵相乘计算因子载荷矩阵
- 【剑指offer】登峰造极--包含min函数的栈
- c#中计算三角形面积公式_三角形面积公式!你到底知道几个?
- 快速云:云管理平台——实现多云部署你需要知道什么
- 抖音盒子、得物、小红书混战社交电商
- Oracle-本地连接没问题,远程连接有问题解决方案
- Jmeter-RandomString和Random函数使用