class类文件结构
目录
- Class 类文件的结构
- 幻数和Class文件版本
- 常量池
- 访问标志
- 类索引
- 父类索引
- 接口索引集合
- 字段表集合
- 访问标识符
- 简单名和描述符
- 方法表集合
- 属性表
- class文件属性表
- 脚注
- 参考资料
Class 类文件的结构
由java源码编译后的class文件包含了Java虚拟机指令和符号表以及若干其他辅助信息。class文件是一组以8位比特(1字节)为单位的二进制流,按照大端顺序存储,严格按照顺序依次排列下去。
class文件采用类似c语言结构体的伪结构存储数据,这种结构只有两种数据类型,无符号数和表。
无符号数是基本数据类型,可以表示整型、浮点型和字符串字面值等。用u1,u2,u4,u8分别表示1个字节,2个字节,4个字节和8个字节的无符号数。
表是由多个无符号数和其他表组合成的复合类型。表类型一般以_info结尾。
整个class文件结构如下表格1:
类型 | 名称 | 数量 |
---|---|---|
u4 | magic | 1 |
u2 | minor_version | 1 |
u2 | major_version | 1 |
u2 | constant_pool_count | 1 |
cp_info | constant_pool | constant_pool_count-1 |
u2 | access_flags | 1 |
u2 | this_class | 1 |
u2 | super_class | 1 |
u2 | interfaces_count | 1 |
u2 | interfaces | interfaces_count |
u2 | fields_count | 1 |
field_info | fields | fields_count |
u2 | methods_count | 1 |
method_info | methods | methods_count |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
class文件开始处四个字节表示magic,紧接着两个字节表示minor_version和两个字节表示major_version,紧接着两个字节表示常量池数目,紧接着是常量池数目-1个不同结构的常量表,依次类推。
写一个简单的程序,编译后分析字节码。
源码:
package com.hnzhrh;public class Simple{private int sum = 0;public int add(int a,int b){sum = a + b;return sum;}
}
得到的class文件以十六进制查看:
幻数和Class文件版本
class文件格式表格第一行,u4类型的magic一个。
0x0000+0~0x0000+3:CA FE BA BE
该值表示该文件是能被JVM接收的class文件。使用幻数区分文件类型而不使用文件后缀名,文件后缀名是可以任意更改的(把jpg改成txt照常还能用画图打开,使用幻数区分)。
class文件格式表格第二行,u2类型的minor_version一个。
0x0000+4~0x0000+5:00 00
表示次版本号为0。
class文件格式表格第三行,u2类型的major_version一个。
0x0000+6~0x0000+7:00 34
表示主版本号为52。
常量池
常量池主要存放两大类常量:字面量和符号引用。
符号引用包括了下面三种常量:
- 类和接口的全限定名
- 字段的名称
- 字段的描述符
- 方法的名称
- 方法的描述符
class文件格式表格第四行,u2类型的constant_pool_count一个。
0x0000+8~0x0000+9:00 13
表示常量池中常量计数,数值为19。但常量项有18个,该计数是从1开始的 。空出来的第0项用来满足特定情况下不引用常量池项目的情况,可以指向常量池0项。
紧接着常量计数之后,就是18个常量项,每个常量项都有自己独立的表结构,表结构的开头tag标志表明了常量类型,如下表2所示:
0x0000+10:0A
查看表可知,tag标志为10(0x0A)的常量类型是CONSTANT_Methodref_info,该类型大小为5字节,所以有:
0x0000+10~0x0000+14 为一个CONSTANT_Methodref_info类型常量,也是一个表结构。
通过表查看CONSTANT_Methodref_info类型的结构,该结构是由u1类型的tag,u2类型的index(指向声明方法的类描述符CONSTANT_Class_info的索引项)和u2类型的index(指向名称及类型描述符CONSTANT_NameAndType的索引项)组成的。
将0x0000+10~0x0000+14划分成三部分内容:
0x0000+10:0A u1类型的tag,值为10。
0x0000+11~0x0000+12:00 04 u2类型的index,值为4。
0x0000+13~0x0000+14:00 0F u2类型的index,值为15。
为了直观,建立一个表格,#+数字表示常量池项的索引,后面单元格存放常量项结构的内容。如下所示:
常量池标号(index) | |||
---|---|---|---|
#0 | |||
#1 | 10 | #4 | #15 |
其余常量项跟上述类似,最后得到的完整的常量池的直观表格:
可读这一列的分析以#1的常量项为例:
至此,常量池的内容结束(0x0000+8~0x0160+5)。
访问标志
class文件格式表格第6行,u2类型的access_flags一个。
0x0160+6~0x0160+7:00 21
访问标志用来表示这个class是类还是接口,是否定义为public,是否定义为abstract等。具有多个属性则将多个标志值或运算得到的结果作为访问标志。
访问标志表3:
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public |
ACC_FINAL | 0x0010 | 是否为final,只有类可设置 |
ACC_SUPER | 0x0020 | JDK 1.0.2之后编译出来的类的这个标志为真 |
ACC_INTERFACE | 0x0200 | 标识是接口 |
ACC_ABSTRACT | 0x0400 | 是否为abstract,接口和抽象类为真 |
ACC_SYNTHETIC | 0x1000 | 标识这个类并非由用户代码产生 |
ACC_ANNOTATION | 0x2000 | 标识这是一个注解 |
ACC_ENUM | 0x4000 | 标识这是一个枚举 |
该class访问标志为0x 0021,表示是一个public类。
类索引
class文件格式表第7行,u2类型的this_class一个。
0x0160+8~0x0160+9:00 03
类索引为0x0003,即指向常量池#3,是一个Class_info类型的常量,表示类或接口的符号引用,该常量的index指向#17,即com/hnzhrh/Simple。
类索引用来确定该类的全限定名。
父类索引
class文件格式表第8行,u2类型的super_class一个。
0x0160+10~0x0160+11:00 04
父类索引为Ox0004,即指向常量池#4,同上分析,得到java/lang/Object。
父类索引用来确定这个类的父类的全限定名。
接口索引集合
class文件格式第9行,u2类型的interface_count一个。
0x0160+12~0x0160+13:00 00
表示接口计数器,该值为0,则该类没有实现接口。所以class文件格式第10行的interfaces也没有。
类索引,父类索引和接口索引集合三者可以表明该类或接口的继承关系。
字段表集合
class文件格式第11行,u2类型的fields_count一个。
0x0160+14~0x0160+15:00 01
表示有1个字段表。
字段表不像常量项那样各有各的结构,字段表的结构是统一的,如下表4所示:
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
类似上述分析方法,得到如下可读表格:
类型 | 名称 | 数量 | 值 | 可读 |
---|---|---|---|---|
u2 | access_flags | 1 | 0x00 02 | private |
u2 | name_index | 1 | 0x00 05 | sum |
u2 | descriptor_index | 1 | 0x00 06 | I |
u2 | attributes_count | 1 | 0x00 00 | |
attribute_info | attributes | attributes_count |
字段类型用于描述接口或者类中声明的变量。
访问标识符
access_flags可以表明字段所具有的特性,access_flags由下表5所示:
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public |
ACC_PRIVATE | 0x0002 | 是否为private |
ACC_PROTECTED | 0x0004 | 是否为protected |
ACC_STATIC | 0x0008 | 是否为static |
ACC_FINAL | 0x0010 | 是否为final |
ACC_VOLATILE | 0x0040 | 是否为volatile |
ACC_TRANSIENT | 0x0080 | 是否为transient |
ACC_SYNTHETIC | 0x1000 | 是否由编译器自动生成 |
ACC_ENUM | 0x4000 | 是否为enum |
简单名和描述符
name_index和descriptor_index两者都为常量池的索引。
name_index为字段的简单名索引(即不带有包名的名称)。
描述符的作用是用来描述字段的数据类型,方法的参数列表(包括数量、类型和顺序)和返回值。
基本数据类型和表示无返回值的void的描述符都用一个大写字母来表示,对象类型则用L加对象的全限定名表示,如下表6:
描述符 | 含义 |
---|---|
B | byte |
C | char |
D | double |
F | float |
I | int |
J | long |
S | short |
Z | boolean |
V | void |
L | 对象类型 |
对于数组类型,使用一个前置的"["来描述,比如定义一个java.lang.String[][]
类型的二维数组,将被记录为[Ljava/lang/String;
。
字段表集合中不会列出从超类或父接口中继承而来的字段,但有可能列出原本Java代码中不存在的字段,比如在内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段。
方法表集合
class文件格式第13行,u2类型的methods_count一个。
0x0176+8~0x0176+9:00 02
表示有两个方法表。
方法表结构与字段表结构一致,如下表7:
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
方法访问标志与字段访问标志略有不同,如下表8:
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public |
ACC_PRIVATE | 0x0002 | 是否为private |
ACC_PROTECTED | 0x0004 | 是否为protected |
ACC_STATIC | 0x0008 | 是否为static |
ACC_FINAL | 0x0010 | 是否为final |
ACC_SYNCHRONIZED | 0x0020 | 是否为synchronized |
ACC_BRIDGE | 0x0040 | 是否为编译器产生的桥接方法 |
ACC_VARARGS | 0x0080 | 是否接受不定参数 |
ACC_NATIVE | 0x0100 | 是否为native |
ACC_ABSTRACT | 0x0400 | 是否为abstract |
ACC_STRICTFP | 0x0800 | 是否为strictfp |
ACC_SYNTHETIC | 0x1000 | 是否由编译器自动生成 |
如果父类方法没有被重写,方发表中不会出现来自父类的方法,也有可能出现编译器自动添加的方法,如类构造器<clinit>
和实列构造器<init>
方法。
开始分析方法表集合:
0x0176+10~0x0176+11:00 01
该方法是public。
0x0176+12~0x0176+13:00 07
简单名索引为7,即<init>
。
0x0176+14~0x0176+5:00 08
描述符索引为8,即V()
。
0x0192+0~0x0192+1:00 01
属性个数为1。
接下来就是一个属性表。
属性表
在class文件、字段表和方法表都可以携带属性表集合,用于描述某些场景专有的信息。
属性表开头都是u2类型的属性名称索引和u4类型的属性长度,紧接着就是不同属性的结构了。
属性表中可能包含其他属性表。
紧接着上述分析:
0x0192+2~0x0192+3:00 09
属性名称索引为9,即Code属性。
0x0192+4~0x0192+7:00 00 00 26
该属性的长度为38字节。
Code属性表9结构:
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | max_stack | 1 |
u2 | max_locals | 1 |
u4 | code_length | 1 |
u1 | code | code_length |
u2 | exception_table_length | 1 |
exception_info | exception_table | exception_table_length |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
同上分析,可以得到:
max_stack:2,max_locals:1
max_stack代表了操作数栈深度的最大值,虚拟机运行时根据这个值来分配栈帧中操作数栈大小。
max_locals代表了局部变量表所需的存储空间,在这里是隐式传递给方法的this参数。
code_length:10
表示字节码长度。
code:10字节长的字节码指令,2A B7 00 01 2A 03 B5 00 02 B1
exception_table_length:0
attributes_count:1
表示Code属性中还有1个属性。
紧接着就是这个属性的名称索引和长度,名称索引:10,即LineNumberTable,长度:10。
LineNumberTable属性用于描述Java源行号和字节码偏移量之间的对应关系。
LineNumberTable属性表10结构:
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | line_number_table_length | 1 |
line_number_info | line_number_table | line_number_table_length |
line_number_info表包含了两个u2类型的数据项,第一个表示字节码行号,第二个表示Java源码行号。
line_number_table_length : 2
line_number_infor: 0 : 3 和 4 : 4。
至此,<init>
方法结束。
开始分析下一个方法,分析方法类似,分析结果如下表:
类型 | 名称 | 值 | 说明 |
---|---|---|---|
u2 | access_flags | 0x00 01 | public |
u2 | name_index | 0x00 0B | add |
u2 | descriptor_index | 0x00 0C | (II)I |
u2 | attributes_count | 0x00 01 | 1个属性 |
u2 | attribute_name_index | 0x00 09 | Code |
u4 | attribute_length | 0x00 00 00 28 | 属性长度40字节 |
u2 | max_stack | 0x00 03 | |
u2 | max_locals | 0x00 03 | |
u4 | code_length | 0x00 00 00 0C | 字节码长度12字节 |
u1 | code | 2A 1B 1C 60 B5 00 02 2A B4 00 02 AC | 字节码 |
u2 | exception_table_length | 0x00 00 | |
exception_info | exception_table | 无 | |
u2 | attributes_count | 0x00 01 | Code属性的属性 |
u2 | attribute_name_index | 0x00 0A | LineNumberTable |
u4 | attribute_length | 0x00 00 00 0A | 属性长度10字节 |
u2 | line_number_table_length | 0x00 02 | 有两个line_number_info |
u2 | start_pc | 0x00 00 | 字节码偏移量 |
u2 | line_number | 0x00 06 | 源码行号 |
u2 | start_pc | 0x00 07 | |
u2 | line_number | 0x00 07 |
class文件属性表
在class文件、字段表和方法表都可以携带属性表集合,用于描述某些场景专有的信息。
属性表开头都是u2类型的属性名称索引和u4类型的属性长度,紧接着就是不同属性的结构了。
属性表中可能包含其他属性表。
class文件格式表格第15行,u2类型的attributes_count一个。
0x0288+4~0x0288+5:00 01
表示class文件有1个属性表。
分析方法类似,结果如下:
类型 | 名称 | 值 | 说明 |
---|---|---|---|
u2 | attribute_name_index | 0x00 0D | SourceFile |
u4 | attribute_length | 0x00 00 00 02 | 属性长度2字节 |
u2 | sourcefile_index | 0x00 0E | Simple.java |
脚注
参考资料
深入理解Java虚拟机 JVM高级特性与最佳实践 周志明著 第二版
表格内容来源于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-1↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-3和表6-6↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-7↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-8↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-9↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-10↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-11↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-12↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-15↩
表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-18↩
转载于:https://www.cnblogs.com/RohanZhang/p/9510863.html
class类文件结构相关推荐
- Class类文件结构、类加载机制以及字节码执行
一.Class类文件结构 Class类文件严格按照顺序紧凑的排列,由无符号数和表构成,表是由多个无符号数或其他数据项构成的符合数据结构. Class类文件格式按如下顺序排列: 类型 名称 数量 u4 ...
- 深入理解JVM虚拟机(四):Class类文件结构(一)
我们都知道Java中的class文件是经过Java编译器对Java类文件进行编译后的产物.我想有不在少数的C程序员在学习Java之后在认知上会粗略的认为C程序在经过编译后产生的.out文件与.clas ...
- 深入理解Java虚拟机知乎_深入理解Java虚拟机(类文件结构)
深入理解Java虚拟机(类文件结构) 欢迎关注微信公众号:BaronTalk,获取更多精彩好文! 之前在阅读 ASM 文档时,对于已编译类的结构.方法描述符.访问标志.ACC_PUBLIC.ACC_P ...
- java class类文件结构
平台无关性 Java是与平台无关的语言,这得益于Java源代码编译后生成的存储字节码的文件,即Class文件,以及Java虚拟机的实现.不仅使用Java编译器可以把Java代码编译成存储字节码 ...
- java主类与源代码名称_Java高级编程基础:类文件结构解析,看穿Class代码背后的秘密...
类文件结构 在说完了JVM内部结构后,接下来我们需要说一下另外一个非常重要的基础概念Class类结构. 我们既然知道了开发的Java源代码会首先被编译成字节码文件保存,JVM的类加载器会读取这些文件内 ...
- Java虚拟机:class类文件结构
一.平台无关性: Java的无关性的实现,是由Java源代码编译后生成的字节码class文件和Java虚拟机实现的.无关性包括:平台无关性以及语言无关性. (1)平台无关性,是指java代码可以运行在 ...
- Class 类文件结构
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17675609 平台无关性 Java是与平台无关的语言,这得益于Java源代码编译后生成的存 ...
- 62.类文件结构(平台无关性、类文件结构)
62.类文件结构 62.1.平台无关性 62.2.类文件结构 62.类文件结构 62.1.平台无关性 Java 是与平台无关的语言,这得益于 Java 源代码编译后生成的存储字节码的文件,即 Clas ...
- 深入理解Java虚拟机(类文件结构)
欢迎关注微信公众号:BaronTalk,获取更多精彩好文! 之前在阅读 ASM 文档时,对于已编译类的结构.方法描述符.访问标志.ACC_PUBLIC.ACC_PRIVATE.各种字节码指令等等许多概 ...
- jvm(6)-java类文件结构(字节码文件)
[0]README 0.1)本文部分文字描述转自 "深入理解jvm",旨在学习类文件结构 的基础知识: 0.2)本文荔枝以及荔枝的分析均为原创: 0.3)下面的截图中有附注t*编 ...
最新文章
- MyBatis 注释
- java商城开发_javaweb实战之商城项目开发(一)
- opencv进阶学习笔记5:图像模糊操作,图像锐化,边缘保留滤波EPF(图像滤镜)
- 账号类型_3-2-3节 文字创作类之账号类型
- 手工搭建APACHE服务
- python一元三次方程拟合_一元三次方程的求根公式
- DotNetCore跨平台~Dockerfile的解释
- 同步数据流语言代码生成工具的研究进展
- python 安装包时出现红字_你好啊!Python,初次见面,多多指教
- java高效获取大文件的行数
- 整洁架构之道--三种经典的编程范式
- [转]MVC+JQuery validate实现用户输入验证
- java读取文件乱码
- 关于python随机抽取各类型不重复值的思考(sample与randint的区别)
- 简单制作登录注册页面
- 学计算机的是不是都非常木讷,北大学神韦东奕​是正常人吗?内向木讷是缺点​,拿不出手?​...
- 【Error】 the public key is not available: NO_PUBKEY 4F4EA0AAE5267A6C
- winform,鼠标移动到label标签时变成手的形状
- iOS textfield 键盘弹出后不能切换中文键盘
- HTML写一个登录框样式
热门文章
- 计算机网络(三)计算机网络-物理层 | 物理设备与传输介质(思维导图 | 知识点总结)
- E: 您必须在 sources.list 中指定代码源(deb-src) URI 解决办法
- JavaScript中的原型,对split方法的重写
- RHEL7配置本地yum源
- 优先级调度算法(C++实现)
- 数组翻转_算法系列之翻转单词顺序
- python正则匹配_python 正则表达式详解
- 使用Markdown写数学公式打出百分号%
- php编译安装memcache,ubuntu源码编译安装memcached和php-memcache 扩展
- 4 关卡流 进阶_儿童桌游要不要鸡血的过关?关卡制儿童桌游介绍与方法论