这一次,彻底弄懂 Java 字节码文件!( 六 )
前面都是铺垫 , 来到重磅分析的一节 。 Java字节码整体结构如下图所示 , 以下图示以不同纬度展示了字节码结构中所包含的关键内容 。 Java字节码整体结构图:
完整的Java字节码结构图:
接下来结合十六进制格式的 class 文件 , 参照 Java字节码文件来剖析下都包含了哪些内容 。 1)4个字节 , Magic Number 魔数 , 值为0xCAFEBABE , 这是Java创始人James Gosling制定 2)2+2个字节 , Version 包括minor_version和major_version , major_version:1.1(45) , 1.2(46) , 1.3(47) , 1.4(48) , 1.5(49) , 1.6(50) , 1.7(51) , 1.8(52) , 1.9(53) , 1.10(54) 3)2+n个字节 , Constant Pool 包括字符串常量、数值常量等 4)2个字节 , Access Flags 访问标记 , 标记当前的类是public、final、abstract等等 , 是不是满足某些特定要求 。 5)2个字节 , This Class Name 当前类的名字 6)2个字节 , Super Class Name 当前类所属父类的名字 7)2+n个字节 , Interfaces 当前类所实现的接口 8)2+n个字节 , Fields 字段表 , 描述了当前类的字段的各种各样的信息 9)2+n个字节 , Methods 方法表 , 当前类所定义的方法 , 这部分内容相对比以上字节结构是比较不容易理解 因为在我们一个类的定义当中方法是最常见的 , 方法里面包含若干的重要信息 , 包含签名、访问修饰符、名字、方法的执行代码逻辑、返回值等等 。 这些方法也是以信息的形式存储在编译之后的字节码class文件当中 , 接下来 , JVM去执行字节码文件时 , 当你调用某个特定方法时 , JVM才能根据你所编写的源代码的意图去执行字节码里的指令 。 对于这个方法来说 , 在JVM中最终是形成一条条指令的去执行的 , 也就是说在字节码里形成的每一条指令对应源码文件中的每一行源代码 。 这些指令也可以称作为助记符 , 比如aload_0 , iload_1等 。 10)2+n个字节 , Attributes 附加属性 Class字节码中有两种数据类型: 字节数据直接量:这是基本的数据类型 。 共细分为u1、u2、u4、u8四种 , 分别代表连续的1个字节、2个字节、4个字节、8个字节组成的整体数据 。 表(数组) , 是一种复合的数据结构 , 表是由多个基本数据或其他表 , 按照既定顺序组成的大的数据集合 。 表是有结构的 , 它的结构体现在:组成表的成分所在的位置和顺序都是已经严格定义好的 。 接下来 , 我们使用javap -verbose命令分析一个字节码文件时 , 将会分析该字节码文件的魔数、版本号、常量池、访问标记、类信息、类变量、类的成员变量、类的构造方法与类中的方法信息等信息 。 4.1 魔数 魔数:所有的.class字节码文件的前4个字节都是魔数 , 文件中魔数为:CA FE BA BE , 魔数值为固定值:0xCAFEBABE(咖啡宝贝) , 这个值的获得很有“浪漫气息” , 其作用是确定这个文件是否为一个能被虚拟机接受的Class文件 。 4.2 版本号 版本号:魔数之后的4个字节为Class文件版本信息 , 前两个字节表示minor version(次版本号) , 后两个字节表示major version(主版本号) 。 这里的版本号为00 00 00 34 , 换算成十进制(3 * 16的1次方 + 4 = 52) , 表示次版本号为0 , 主版本号为52 。 所以 , 该文件的版本号为:1.8.0 。 可以通过java -version命令来验证这一点 。 Java的版本号是从45开始的 , JDK1.0之后大的主版本号线上加1 , 如JDK1.1(45)、JDK1.2(46)以此类推JDK1.8(52) 。 4.3 常量池 常量池(constant pool):紧接着主版本号之后的就是常量池入口 。 一个Java类中定义的很多信息都是由常量池来维护和描述的 , 可以将常量池看作是Class文件的资源仓库 , 比如说Java类中定义的方法与变量信息 , 都是存储在常量池中 。 由于常量池中常量的数量是不固定的 , 故在常量池入口需要放置一项u2类型的数据 , 代表常量池容量计数值(constant_pool_count) 。 这里的容量计数是从1开始的 , 十六进制数为:00 18 , 转换为十进制为24 , 代表常量池中有24项常量 , 索引值范围1~24 。 常量池数组中元素的个数 = 常量池数 - 1(其中0暂时不使用) , 所以Java字节码文件中constant_pool中只看到了23项目常量 。 那为什么容量计数不从0开始呢?具体原因下一节说明 。 常量池中主要存储两类常量: 字面量:字面量如文本字符串 , Java中声明为final的常量值等 。 符号引用:类和接口的全局限定名 , 字段的名称和描述符 , 方法的名称和描述符等 。 4.3.1 常量池总体结构 Java类所对应的常量池主要由常量池数量与常量池数组(常量表)这两部分共同构成 。 常量池数量紧跟在主版本号后面 , 占据2个字节;常量池数组紧跟在常量池数量之后 。 常量池数组与一般的数组不同的是 , 常量池数组中元素的类型、结构都是不同的 , 长度当然也就不同;但是 , 每一种元素第一个数据都是一个u1类型 , 该字节是个标志位 , 占据1个字节 。 JVM在解析常量池时 , 会根据这个u1类型来获取元素的具体类型 。 值得注意的是 , 常量池数组中元素的个数 = 常量池数 - 1(其中0暂时不使用) , 目的是满足某些常量池索引值的数据在特定情况下需要表达「不引用任何一个常量池」的含义;根本原因在于 , 索引为0也是一个常量(保留常量) , 只不过它不位于常量表中 , 这个常量就对应null值;所以 , 常量池的索引从1而非0开始 。 4.3.2 常量池项目类型 Class文件结构中常量池中实际是有14种数据类型的 , 12~14种数据类型是在JDK1.7之后添加进来的(新增三种类型分别为:CONSTANT_MethodHandle_info、CONSTANT_MethodType_info、CONSTANT_InvokeDynamic_info) , 主要是为了更好的支持动态语言调用的 。 但是 , 最常用如下所列的列出了11种常规的数据类型:
推荐阅读
- 暴雪|暴雪新总裁刚上任3个月就离职,这一次,暴雪还有救吗
- 魔兽世界怀旧服|制霸全明星返场彻底凉了,上架第一天销量惨淡,玩家:有赛季皮肤足够了
- 明日方舟|明日方舟:六星近卫斯卡蒂现版本被完全撕卡 SP临光彻底让其失业
- 赛尔号|赛尔号衍生游戏精灵拟人形象 童年情怀彻底没了 但玩家有了新老婆
- 王者荣耀|王者荣耀:孙行者四款皮肤海报全部曝光,这一次倒是没让玩家失望
- 手机游戏|LOL手游传奇杯WT拿下冠军!意外惊现同阵容!版本答案彻底揭晓
- 极限国度|阴阳师川猿超鬼王活动攻略 活动过于阳间 爆肝删好友时代彻底终结
- 堡垒之夜|《堡垒之夜》国服关服,腾讯运营三年以彻底失败告终
- t1|T1上单鼠男账号被爆破,评论内容简单粗暴,一场BO5让他彻底成名
- 舰娘|碧蓝航线官方最大的强度笑话 未来可期的阿尔及利亚彻底卷没了