JVM学习笔记(7) 第三部分 虚拟机执行子系统 第6章 类文件结构
文章目录第6章 类文件结构6.0 个人感悟6.1 概述6.2 无关性的基石6.3 Class类文件的结构6.3.1 魔数与版本号6.3.2 常量池6.3.3 访问标志6.3.4 类索引、父类索引与接口索引集合6.3.5 字段表集合6.3.6 方法表集合6.3.7 属性表集合代码编译的结果从本地机器码转变为字节码是存储格式发展的一小步却是编程语言进步的一大步。 — 《深入理解Java虚拟机》第3版 周志明第6章 类文件结构6.0 个人感悟Class文件的巧妙设计软件工程中有句经典的话任何问题都可以通过增加一个中间层来解决。字节码文件就是硬件和高级语言之间的那个中间层。开发人员只用关注语言使用各种语法、语法糖,不用关注到底怎么执行编译部署平台无关字节码可以通过JVM到处运行JVM语言无关无论哪种语言符合class格式就认向后兼容性的好处时至今日Class文件格式虽然多次更新但基本只是在原有结构基础上新增内容、扩充功能并未对已定义的内容做修改。这种设计保证了Java技术一直保持良好的向后兼容性。实践建议如果本章内容感觉抽象建议动手尝试使用javap -verbose YourClass.class反编译对照本章结构理解用十六进制编辑器直接打开.class文件逐字节解读尝试编写一个简单的Class文件解析程序加深对格式的理解6.1 概述本章围绕一个核心问题展开A: Java源代码.java文件经过编译后究竟变成了什么Q: 答案就是Class文件也叫字节码文件。虚拟机并不关心这个Class文件是从哪种语言编译来的,它只认这个统一的二进制格式。6.2 无关性的基石平台无关性:“一次编写到处运行”Write Once, Run Anywhere是Java诞生之初的口号。这个理想最终实现在操作系统应用层上各种不同平台的Java虚拟机都可以载入和执行同一种平台无关的字节码从而实现了程序的跨平台运行。这里的字节码就是构成平台无关性的基石。语言无关性:JVM只认Class文件不管它是Java编译来的还是Kotlin、Scala编译来的。6.3 Class类文件的结构整体特点Class文件是以8字节为基础单位的二进制流各个数据项目严格按顺序紧凑排列中间没有任何分隔符。当遇到需要占用8个字节以上空间的数据项时按照高位在前大端模式的方式分割成若干个8字节进行存储。Class文件格式采用类似于C语言结构体的伪结构来存储数据。两种数据类型无符号数和表无符号数基本数据类型用u1、u2、u4、u8分别代表1、2、4、8个字节的无符号数可描述数字、索引引用、数量值或UTF-8字符串表由多个无符号数或其他表构成的复合数据类型所有表命名都以“_info”结尾如cp_info、field_info、method_info等。整个Class文件本质上也可看作一张表。当需要描述同一类型但数量不定的多个数据时Class文件格式会使用“前置容量计数器 若干个连续数据项”的形式称为“集合”ClassFile结构体总览根据《Java虚拟机规范》Class文件的格式严格限定如下表所示类型名称数量说明u4magic1魔数u2minor_version1次版本号u2major_version1主版本号u2constant_pool_count1常量池计数器cp_infoconstant_pool[constant_pool_count-1]N常量池u2access_flags1访问标志u2this_class1类索引u2super_class1父类索引u2interfaces_count1接口计数器u2interfaces[interfaces_count]N接口索引集合u2fields_count1字段计数器field_infofields[fields_count]N字段表集合u2methods_count1方法计数器method_infomethods[methods_count]N方法表集合u2attributes_count1属性计数器attribute_infoattributes[attributes_count]N属性表集合6.3.1 魔数与版本号魔数头4个字节固定值0xCAFEBABE咖啡宝贝用于确定文件是否为合法的Class文件。使用魔数而非扩展名是出于安全考虑。版本号紧接着的4个字节第5、6为次版本号第7、8为主版本号高版本JDK能向下兼容以前版本的Class文件但不能运行以后版本的Class文件JDK 8 → 主版本号 520x0034JDK 11 → 550x0037JDK 12之后次版本号为65535时表示“技术预览版”功能6.3.2 常量池常量池是Class文件中与其他项目关联最多的数据也是第一个出现的表类型数据项目可理解为“资源仓库”。常量池计数器u2类型从1开始计数第0项留空表示“不引用任何常量池项目”。两大类常量字面量如文本字符串、final常量值等。符号引用包括包、类和接口的全限定名、字段名称和描述符、方法名称和描述符、方法句柄、动态调用点等。常量池中每一项都以一个u1类型的tag开头JDK 1.7之后共有14种不同的表结构。6.3.3 访问标志常量池之后的两个字节access_flags用于识别类或接口层次的访问信息是类还是接口是否为public是否为abstract如果是类是否声明为final共有16个标志位当前定义了8个未使用的必须为06.3.4 类索引、父类索引与接口索引集合类索引this_class确定类的全限定名。父类索引super_class确定父类的全限定名。接口索引集合interfaces[]描述该类实现的接口按implements语句从左到右排列。6.3.5 字段表集合字段表field_info描述接口或类中声明的变量类级变量和实例级变量不包括方法内的局部变量。字段表集合不会列出从超类或父类接口继承的字段。字段表结构包含访问标志、名称索引、描述符索引、属性表集合。名称索引和描述符索引指向常量池中的字符串常量。6.3.6 方法表集合方法表method_info结构与字段表类似但访问标志中没有volatile和transient对方法无意义方法表结构包含访问标志、名称索引、描述符索引、属性表集合。字节码指令存储位置方法表的属性表集合中的Code属性——这里存放着真正的字节码指令序列。这也印证了字节码指令是字节码文件的核心内容存在于每个方法的Code属性中。6.3.7 属性表集合属性表attribute_info是Class文件中最灵活的部分在Class文件、字段表、方法表中都可携带用于描述某些场景专有的信息。与Class文件其他部分严格顺序不同属性表集合的顺序要求较宽松常见属性Code属性存储方法体的字节码指令最关键。Exceptions属性方法抛出的异常。LineNumberTable属性源码行号与字节码指令的对应关系用于调试。LocalVariableTable属性方法局部变量的描述。SourceFile属性源文件名。ConstantValue属性final常量值的初始化。InnerClasses属性内部类信息。Deprecated及Synthetic属性。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2503446.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!