JVM-9.Class类文件结构

  • Class 文件是一组以8个字节为基础额二级制流
  • 各数据项目严格按照顺序紧凑的排列在Class文件中,中间没有任何分隔符
  • Class文件的伪结构只包含两种数据类型:
    • 无符号数,以u1、u2、u4、u8来代表1个字节,2个字节,4个字节,8个字节的无符号数,用来描述数字,索引引用,数量值或者按照UTF-8编码构成字符串值
    • 表,由多个无符号数或者其他表作为数据项构成的复合数据类型,以_info结尾,用于描述具有层次关系的复合结构数据
  • 整个Class文件本质上就是一张表:
名称 类型 数量
magic u4 1
minor_version u2 1
major_version u2 1
constant_pool_count u2 1
constant_pool cp_info constant_pool_count-1
access_flags u2 1
this_class u2 1
super_class u2 1
interfaces_count u2 1
interfaces u2 interfaces_count
fields_count u2 1
fields field_info fields_count
u2 methods_count 1
methods method_info methods_count
attributes_count u2 1
attributeds attributed_info attributes_count

1. magic(魔法数)

  • 用于确认这个文件是否为一个能被虚拟机接受的Class文件。
  • 值为:0xCAFEBABE-(咖啡宝贝)

2. 版本号

  • minor_version 次版本号
  • major_version 主版本号
  • JDK向下兼容,向上不兼容

3. 常量池

  • constant_pool_count:常量池容量计数值
    • 计数从1开始,第0项空出来
  • 常量池主要用来存放字面量(Literal)和符号引用(Symbolic Reference)
    • 字面量如文本字符串,声明为final的常量
    • 符号引用包括:
      • 类和接口的全限定名(Full Qualified Name)
      • 字段的名称和描述符(Descriptor)
      • 方法的名称和描述符
  • 常量池中每一个常量都是一个表
    • 表结构各不相同
    • 都有一个u1类型的标志位(tag),代表当前常量属于的数据类型
  • javap -verbose Class可以输出常量表
常量池中常量项结构总表1

常量池中常量项结构总表2

4. 访问标志

访问标志(access_flag)用来识别一些类或者接口层次的访问信息:


访问标志

5. 类索引、父类索引和接口索引集合

  • 类索引(this_class)、父类索引(super_class)都是u2类型数据,接口索引(interfaces)是一组u2类型的集合。
  • 这三个数据来确定这个类的继承关系。
    • 类索引用于确定这个类的全限定名
    • 父类索引用于确认这个类的父类全限定名
      • 除了java.lang.Object之外的Java类都有父类,因此除了它之外的所有Java类的父类索引都不为0
  • 接口索引描述了类实现的接口按implements(或extends)后的接口顺序从左到右排列在索引集合中

6. 字段表集合

  • 字段表(field_info)用于描述接口或者类中声明的变量。

  • 包含类级变量以及实例级变量,不包括方法内部声明的局部变量

  • 变量修饰符用布尔值标识,变量名、数据类型用常量池中的常量表示

  • 字段表结构:


    字段表结构

    -access_flag是字段修饰符,取值如下:


    字段访问标志
    • name_index和descriptor_index分别标识字段简单名称和字段方法的描述符:
      • 每一维度的数组类型用前值[字符表示
      • 描述方法按照先参数列表,后返回值的顺序,参数列表按照顺序放在一组小括号()内。
      • 在descriptor_index之后都跟随者一个属性列表集合用于存储额外信息
      • 字段表中不会列出从超类或者父接口中继承来的字段,但可能列出自动添加的额外字段


        描述符字段含义

7. 方法表集合

  • 方法表的结构和字段表接口类似,访问标志和属性表集合的可选项有区别
  • 方法里的代码,经过编译器编译成字节码指令后,存放在方法属性表集合的Code属性中
  • 有可能出现编译器自动添加的方法,例如实例构造器


    方法表结构
方法访问标志

8. 属性表集合

  • 属性表(attribute_info),在Class文件,字段表,方法表都可以携带自己的属性表集合,用于描述场景专有信息
  • 属性表不要求具有严格顺序
  • 不与已有属性名重复,任何编译器都可以向属性表中写入自定义属性信息,JVM会自动忽略不认识的属性


    JVM预定义属性1

    JVM预定义属性2
  • 每个属性都需要从常量池中引入一个CONSTANT_Utf8_info类型常量来表示
  • 属性值的结构完全自定义,只需要通过一个u4的长度属性来说明属性值占用位数
  • 属性表结构:


    属性表结构

8.1 Code属性

  • 方法体经过javac编译后,变成字节码存储在Code属性内
  • Code属性出现在方法表的属性集合中,接口或抽象类中的方法没有Code属性
  • Code属性表结构:


    Code属性表结构
    • attribute_name_index指向CONSTANT_Utf8_info型常量的索引,固定值为“Code”
    • attribute_length指示了属性值的长度,和attribute_name_index一共6字节,所以属性值的长度固定位整个属性表长度减去6字节
    • max_stack标识操作数栈(Operand Stacks)深度的最大值,在方法执行的任意时刻,操作数栈不会超过这个深度,JVM据此分配栈帧(Stack Frame)中的栈操作深度
    • max_locals代表局部变量表需要的存储空间,
      • 此值单位是Slot,长度不超过32位的数据类型用1个Slot,double和long 64位的数据类型需要两个Slot
      • 方法参数,显式异常处理的参数,方法体中定义的局部变量都在需要局部变量表存放
      • Slot可以重用,根据变量作用于分配,得到max_locals
    • code_length和code用来存放Java源程序编译后生成的字节码长度和字节流
      • 一个指令就是一个u1类型单字节,对应一个指令,后面可能跟参数
      • 如果有异常表,表结构为:
        -标识字节码在start_pc行(行,指字节码相对于方法体开始的偏移量)到第end_pc行(不含)之间出现了类型为catch_type或者其子类的异常,就转到第handler_pc行继续处理
      • catch_type值可以为0,此时任何异常情况都转到handler_pc行


        异常表属性结构

8.2 Exceptions属性

  • Exceptions属性不是Code属性中的异常表
  • 它的作用是列举方法中可能抛出的受检异常(Checked Exceptions)
  • 也就是throws关键字之后的异常
  • 表结构:


    受检异常表结构
    • number_of_exceptions标识方法可能抛出的异常种数
    • exception_index_table标识一种受检异常,是一个指向常量池中CONSTANT_Class_info型常量的索引

8.3 LineNumberTable属性

  • 用于描述Java源码行号和字节码行号(偏移量)之间的对应关系
  • 可以在javac中用 -g:none或者-g:lines选项来取消或者要求生成这项信息,默认生成,取消后抛出异常时堆栈无法显示出错的行号。也无法按照源码设置断点
  • 表结构为:


    LineNumberTable属性结构
    • line_number_table是一个数量为line_number_table_length,类型为line_number_info的集合,
    • line_number_info表包括start_pc和line_number两个u2类型数据项,前者是字节码行号,后者是Java源码行号

8.4 LocalVariableTable属性

  • 用于描述栈帧中局部变量和Java源码中定义的变量之间的关系
  • 默认生成到Class文件之中,可以在javac中使用-g:none或者-g:vars取消或者要求生成。如果取消,所有参数名称会消失
  • 属性表结构:


    LocalVariableTable属性表结构
    • 其中的local_variable_info项目代表了栈帧和源码中的局部变量的关联,其表结构为:


      local_variable_info项目结构
      • start_pc和length表示这个局部变量的生命周期开始的字节码偏移量和作用域范围长度
      • name_index和descriptor_index指向CONSTANT_Utf8_info型常量索引,标识局部变量的名称和它的描述符
      • index是这个局部变量在栈帧局部变量中Slot位置

8.5 SourceFile属性

  • 用于记录生成这个Class文件的源码名称
  • 可选 -g:none-g:source
  • 定长属性,结构为:


    SourceFile属性结构
    • sourcefile_index:指向常量池中CONSTANT_Utf8_info,其值为源码文件的文件名

8.6 ConstantValue属性

  • 通知JVM自动为静态变量赋值
  • 也就是static修饰的变量
  • 表结构:


    ConstantValue属性结构

8.7 InnerClass属性

  • 用于记录内部类和宿主类之间的关系
  • 结构:


    InnerClass属性结构
    • number_of_classes标识需要记录的内部类个数
    • 一个内部类一个inner_class_info表:


      inner_classes_info表结构
      • inner_class_info_index和outer_class_info_index都是指向常量池中CONSTANT_Class_info型的常量索引,分别代表内部类和宿主类的符号引用
      • inner_name_index指向CONSTANT_Utf8_info型,内部类名称,匿名内部类为0
      • inner_class_access_flags:内部类反问标志,取值范围:


        inner_class_access_flags标志

8.8 Deprecated和Synthetic属性

  • Deprecated:用于类,字段,方法,在代码中用@deprecated注解进行设置,标识不推荐使用
  • Synthetic:标识字段,方法不是Java源码,是编译器自动生成的
  • 结构都是:


    属性结构
    • attribute_length值为0x00000000

8.9 StackMapTable属性

  • 在JVM类加载的字节码验证阶段被新类型验证器(Type Checker)使用,目的在与代替之前比较消耗性能的基于数据流分析的类型推导验证器
  • 暂时看不懂

8.10 Signature属性

  • 可选定长属性
  • 出现在类,属性表,方法表结构的属性表中
  • 记录泛型签名信息
  • 表结构:


    Signature属性结构
    • signature_index对常量池的索引,CONSTANT_Utf8_info型,表示类签名,方法类型签名或者字段类型签名

8.11 BootstrapMethods属性

  • 用于保存invokedynamic指令引用的引导方法限定符
  • 结构:


    BootstrapMethods属性结构
    • bootstrap_methods结构:


      bootstrap_method属性的结构
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • 类文件结构 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式— 字节码( ByteCode ) 是构成平台无...
    好好学习Sun阅读 530评论 0 0
  • 在说道 JVM 虚拟机的时候,很多人都会想到 Java 语言,诚然,Java 语言和 JVM 虚拟机息息相关,但是...
    lijiankun24阅读 11,619评论 9 31
  •  每一个class文件都对应着唯一一个类或者接口的定义信息,但是相对地,类或者接口并不一定都必须定义在文件里(比如...
    SunnyMore阅读 6,108评论 0 1
  • 1. Get up, step out, and live fully. I once passed a bill...
    DavidLam阅读 134评论 0 0
  • 温一壶百年普洱,弹几把扬琴琵琶。在这场思无邪的诗卷里,是谁在杨柳依依之时负命出征?点点滴滴,终化成风。 ...
    书山图阅读 360评论 0 0