类文件的结构

Class类文件是以8个字节为单位的二进制流,由魔数、版本号、常量池、类信息、父类信息、接口表、字段表、方法表和属性表组成。下图清晰的展示了Class类文件的结构。

Class类文件示例

预先准备好一段简单的Java代码和编译好的二进制字节流。

Class类文件是如何组成的

接下来会用上述简单的Java代码为示例来讲解Class类文件是如何组成的?

魔数和版本号

魔数是用来检查字节流是不是Class类文件,占4个字节。Class类文件的魔数是cafebabe
查看偏移地址0x00000000-0x00000003,得到魔数是0xcafebabe,可以得出该字节流是一个Class类文件。

版本号是指Jdk的版本号,占4个字节。前2个字节是次版本号,后2个字节是主版本号。
查看偏移地址0x00000004-0x00000007,得到次版本号是0(=0x0000),主版本号是52(=0x0034),也就是版本号是52.0,对应的是jdk8。

常量池

常量个数指常量池中有多少个常量,占2个字节。
查看偏移地址0x00000008-0x00000009,得到该常量池中一共有22(=0x17-1)个常量。减1的原因是常量是从1开始计数的。

常量指名称(类名、字段名、方法名等)或描述符的字面量或符号引用。

  • 字面量可以理解为不经过翻译的字符串。比如Java文件中的类名Hello字符串;比如字段名a、b、m字符串;比如方法名inc字符串;比如返回类型int字符串;
  • 符号引用可以理解为由标签和索引(指向字面量或标签)组成。比如类和接口的全限定名;比如字段的名称和描述符;比如方法的名称和描述符。

接下来我们看下这22个常量在字节流中是怎样体现的?查找常量就像密码学里根据密码表找到对应的密码一样。首先准备好定义好的17种数据结构,为了节省篇幅,这17种数据结构请读者参考《深入理解Java虚拟机第3版》常量池一节的表6.6。这里仅列出4种数据结构,下图右边部分。

上图左边部分已根据定义好的17种数据结构列出了Hello.class的22个常量项(#号开头的数字),现以第一个常量项为例来进行讲解。

  • 第1号常量项的偏移地址0x0000000A的值为10(=0x0a),对应的是CONSTANT_Methodref_info标签。根据该标签的数据结构可得,一共占5个字节,第1个字节就是前面说的标签;第2-3个字节表示该方法属于哪个类,这里指向的是第4号常量项;第4-5字节表示该方法的名称和描述符(指返回类型),这里指向的是第19号常量项。
  • 第4号常量项的偏移地址0x00000017的值为7(=0x07),对应的是CONSTANT_Class_info标签。根据该标签的数据结构可得,一共占3个字节,第1个字节就是前面说的标签;第2-3个字节表示该方法所在类的全限定名,这里指向的是第22号常量项。
  • 第22号常量项的偏移地址0x0000009F的值为1(=0x01),对应的是CONSTANT_Utf8_info标签。根据该标签的数据结构可得,一共占6个字节,第1个字节就是前面说的标签;第2-3个字节表示方法名的长度,这里的值为16个字节,接下来的16个字节清晰的展示了类的全限定名(java/lang/Object)。
  • 第19号常量项的偏移地址0x0000008D的值为12(=0x0c),对应的是CONSTANT_NameAndType_info标签。根据该标签的数据结构可得,一共占5个字节,第1个字节就是前面说的标签;第2-3个字节表示该方法的名称,这里指向的是第11号常量项;第4-5字节表示该方法的描述符,这里指向的是第12号常量项。
  • 第11号常量项的偏移地址0x0000003F的值为1(=0x01),对应的是CONSTANT_Utf8_info标签。与第22号常量项类似,最后清晰的展示了方法名称(<init> ,即构造器)。
  • 第12号常量项的偏移地址0x00000048的值为1(=0x01),对应的是CONSTANT_Utf8_info标签。与第22号常量项类似,最后清晰的展示了方法描述符(()V ,即void)。

最后通过javap命令让我们更直观的感受下常量池。

类信息和父类信息

类信息由类访问符类名索引父类名索引组成,他们各占2个字节。

查看偏移地址0x000000b2-0x000000b3,得到值为0x0021(=0x0001+0x0020),查表可得类访问符为public super。
查看偏移地址0x000000b4-0x000000b5,得到值为3(=0x0003),表示指向第3号常量项(Hello)。
查看偏移地址0x000000b6-0x000000b7,得到值为4(=0x0004),表示指向第4号常量项(java/lang/Object)。

接口表

接口数量占用2个字节。查看偏移地址0x000000b8-0x000000b9,得到值为0,也就是说该类没有实现相关接口。
接口表描述了接口相关信息。

字段表

字段数量占用2个字节。查看偏移地址0x000000bA-0x000000bB,得到值为3(=0x0003),也就是说该类中有3个字段。
字段表描述了字段相关信息。

接下来我们根据字段表结构来读下Hello.class字节流中的3个字段。

上图中已经标示了字节流中的3个字段(^角开头的数字)。现以第一个字段为例来进行讲解。

  • 偏移地址0x000000bC-0x000000bD的值为0x001a(=0x0002+0x0008+0x0010),定义了字段的访问符,查找字段访问符表可知,该字段由private static final修饰。
  • 偏移地址0x000000bE-0x000000bF的值为5(=0x0005),定义了字段名称索引,表示指向第5个常量项(a)
  • 偏移地址0x000000c0-0x000000c1的值为6(=0x0006),定义了字段描述符索引,表示指向第6个常量项(I,即int)
  • 偏移地址0x000000c2-0x000000cB定义了该字段的属性,属性表稍后讲解。

方法表

方法数量占用2个字节。查看偏移地址0x000000dC-0x000000dD,得到值为2(=0x0002),也就是说该类中有2个方法。
方法表描述了方法相关信息。

接下来我们根据方法表结构来读下Hello.class字节流中的2个方法。

上图中已经标示了字节流中的2个方法(^角开头的数字)。现以第一个方法为例来进行讲解。

  • 偏移地址0x000000dE-0x000000dF的值为0x0001,定义了方法的访问符,查找方法访问符表可知,该方法由public修饰。
  • 偏移地址0x000000e0-0x000000e1的值为11(=0x000b),定义了方法名称索引,表示指向第11个常量项(<init> ,即构造器)。
  • 偏移地址0x000000e2-0x000000e3的值为12(=0x000c),定义了方法描述符索引,表示指向第12个常量项(()V,即void)
  • 偏移地址0x000000e4-0x000000fA定义了该方法的属性,属性表稍后讲解。

最后通过javap命令让我们更直观的感受下方法表。

属性表

ConstantValue属性一般定义在字段表中。下图展示了ConstantValue属性的数据结构。

在字段表中说过,偏移地址0x000000c2-0x000000cB定义了字段a的属性。

  • 偏移地址0x000000c2-0x000000c3的值为1(=0x0001),定义了该字段属性的个数。
  • 偏移地址0x000000c4-0x000000c5的值为7(=0x0007),定义了该字段属性名称的索引,表示指向第7个常量项(ConstantValue)。
  • 偏移地址0x000000c6-0x000000c9的值为2(=0x0002),定义了该属性的长度,表示该属性一共占用2个字节。
  • 偏移地址0x000000ca-0x000000cb的值为8(=0x0008),定义了该属性的值,表示指向第8个常量项(属性值为10)。

Code属性一般定义在字段表中。下图展示了Code属性的数据结构。

在方法表中说过,偏移地址0x000000e4-0x000000fA定义了<init>方法的属性。

  • 偏移地址0x000000e4-0x000000e5的值为1(=0x0001),定义了该方法属性的个数。

  • 偏移地址0x000000e6-0x000000e7的值为13(=0x000d),定义了该方法属性名称的索引,表示指向第13个常量项(Code)。

  • 偏移地址0x000000e8-0x000000eB的值为29(=0x001d),定义了该属性的长度,表示该属性一共占用29个字节。

  • 偏移地址0x000000eC-0x000000eD的值为1(=0x0001),定义了该方法操作数栈深度。

  • 偏移地址0x000000eE-0x000000eF的值为1(=0x0001),定义了该方法局部变量表大小。

  • 偏移地址0x000000f0-0x000000f3的值为5(=0x0005),定义了方法体的长度。

  • 偏移地址0x000000f4-0x000000f8的值为0x2ab70001b1,定义了方法体的指令,这些指令都可以在虚拟机字节码指令表中查到。
    1)读入2a,查表得0x2a对应的指令为aload_0,表示将第0个变量槽中reference类型(指this)推送到操作数栈顶。
    2)读入b7,查表得0xb7对应的指令为invokespecial,invokespecial用于调用实例构造器、private方法或者它的父类的方法。读入0001,指向第1个常量项,表示invokespecial调用的是实例构造器。
    3)读入b1,查表得0xb1对应的指令为return,表示方法正常结束。

  • 偏移地址0x000000f9-0x0000108,依次定义了异常表、LineNumberTable属性,这里不作进一步讲解。

为了节省篇幅,属性表中只介绍ConstantValue属性和Code属性两种比较重要的属性,其他的属性,比如Exceptions属性、SourceFile属性等,有兴趣的朋友可以阅读《深入理解Java虚拟机第3版》属性表一节。

Reference

深入理解Java虚拟机第3版
Java虚拟机原理图解

深入理解JVM - 类文件的结构相关推荐

  1. Class类文件的结构

    2019独角兽企业重金招聘Python工程师标准>>> Class类文件的结构 Class类文件的结构 任何一个Class文件都对应着唯一一个类或接口的定义信息,但反之类和接口并不一 ...

  2. 详解Class类文件的结构(上)

    前言 相信搞Java开发的同学都经常会接触到Class类文件,了解了JVM虚拟机之后也会大量接触到class字节码,那么它到底是什么样的文件?内部由什么构成?虚拟机又是如何去识别它的?这篇文章就来学习 ...

  3. java怎编写么解析一个类型_DAY3:你必须知道的java虚拟机之类篇——类文件的结构...

    马上过年啦,不知道大家今年有没有投资基金股票呢?是赚的盆满钵满还是拍断大腿,可以评论区一起交流交流,秀一秀哈哈,反正我是没来得及上车. 暴富西不可能暴富的啦,打工人嘛几能写写文章啦-记得点赞➕关注呀 ...

  4. [深入理解Java虚拟机]第六章 Class类文件的结构

    在本章关于Class文件结构的讲解中,我们将以<Java虚拟机规范(第2版 )> (1999年发布,对应于JDK 1.4时代的Java虚拟机)中的定义为主线,这部分内容虽然古老,但它所包含 ...

  5. 深入理解JVM类文件格式

    我们知道Java最有名的宣传口号就是:"一次编写,到处运行(Write Once,Run Anywhere)",而其平台无关性则是依赖于JVM, 所有的java文件都被编译成字节码 ...

  6. 详解Class类文件的结构(下)

    本文继续使用上次的Test.class文件,它是由下面单独的一个类文件编译而成的,没有包. 6. 索引(Index) 索引又分类索引.父类索引和接口索引集合,类索引(this_class)和父类索引( ...

  7. Java Class类文件的结构

    基础概念 任何一个 Class 文件都对应着唯一的一个类或接口的信息 Class 文件是一组以字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在文件之中,中间没有添加任何分隔符.这使得整个 ...

  8. JVM——类文件结构

    1.概述 老师讲过:"计算机只认识0和1,所以我们写的程序需要经编译器译成由0和1构成的二进制格才能由计算机执行".到由于最近10年内虚拟机以及大量建立再虚拟机之上的程序语言如雨后 ...

  9. 【转】深入理解JVM—JVM内存模型

    原文链接 http://www.cnblogs.com/dingyingsi/p/3760447.html#top 深入理解JVM-JVM内存模型 我们知道,计算机CPU和内存的交互是最频繁的,内存是 ...

  10. 深入理解JVM(1):类加载器

    文章目录 一.类加载简介 1.简介 2.Java虚拟机与程序的生命周期 3.类的加载.连接与初始化(类加载的最重要的3个阶段) 3.1加载 3.2连接 3.3 初始化 4.类的使用和卸载(类加载的剩余 ...

最新文章

  1. html怎么移动文字的位置,css怎么移动文字
  2. nbiot开发需要掌握什么_包装设计需要掌握什么技巧
  3. 分段概率密度矩估计_考研数学:高数、线代、概率3科目知识框架梳理
  4. 小夕说,不了解动态空间增长的程序喵都是假喵(下)
  5. html%2b怎么转换成加号,Apache mod_rewrite%2B和加号(+)符号
  6. 常见面试算法:树回归、树剪枝
  7. unhandled exception in MSDEV.EXE(DEVSHL.DLL) :0xC0000005
  8. miui9如何不自动杀进程,小米9怎么关闭自动更新 具体操作方法解析
  9. 【大数据技术】实验4:熟悉Spark基础编程
  10. 从龟速 11s 到闪电 1s,详解前端性能优化之首屏加载
  11. Linux内核PWN-基础ROP提权
  12. 3D数学基础——欧拉角与万向节死锁
  13. MongoDB之文本搜索
  14. 理解偏差和方差(Bias-Variance)的Tradeoff
  15. 计算机打印机共享失败,电脑共享打印连接错误怎么回事 电脑打印共享错误的解决方法...
  16. AutoJS一文精通AutoJS脚本教程详解
  17. UCI 数据集 - http://www.mafutian.net/180.html
  18. 阿里云接口----OSS文件上传
  19. 交易猫源码搭建+完整版源码
  20. Source Insight 将UTF-8格式的JAVA文件转换为GB2312格式的JAVA文件

热门文章

  1. Android系统WebView无法安装,Android系统进程无法加载WebView
  2. 运动神经元病是怎么回事?
  3. 金融管理系统测试报告(内部测试案例)
  4. 各移动云测试平台对比
  5. Web Strom 必备快捷键
  6. 虚假评价检测(Spam Detection)最新工作综述
  7. python学习之类
  8. 18获得触发事件元素节点的方法
  9. 运动会加油稿计算机学院,学院运动会加油稿范文
  10. oracle的解除锁表