java字节码结构模型
前言
阅读对象
本文是博主根据自己经验和查阅资料完成,如有不正确之处欢迎指正。时间宝贵如果你对字节码内幕不敢兴趣,不建议你往下读(不如去研究一下那个英雄更容易上分)。
目标
- 了解字节码的意义
- 了解字节码如何存储
- 了解字节码的内容
字节码结构
技术枯燥,坚持胜利。
背景
学过java我们知道,虚拟机是跨平台技术的一种成熟的解决方案。 而字节码技术是为了同一个虚拟机支持不同语言的解决方案。不严谨的说虚拟机只认识字节码,任何语言只要能编译为字节码都可以在虚拟机上运行(比如:Java、Kotlin、Clojure、Groovy、JRuby、JPython、Scala等语言)。所以字节技术才是跨平台语言诞生的基石,下面用java语言来讲解。
Class文件
一个.class文件表示一个类或者接口,但是一个类或接口不一定要用.class文件类表示(可通过即时编译器生成字节流通过类加载器直接加载使用,java的jdk代理是通过这样的技术来实现)。
文件结构
我们都知道字节码文件是由.class后缀表示的。文件内容是一组8个字节为单位的二进制流,各个数据严格按照顺序紧凑排列在文件之中,中间没有任何空隙和分隔符。根据《java虚拟机规范》这些字节流是用过“无符号数”和“表”两种形式来存储类或者接口的信息。
无符号数
无符号数是属于基本数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节、8个字节。可以用来表示数字、索引引用、数量值和按照UTF-8编码生成的字符串。
表
表是由无符号数和其他表作为数据项构成的复合数据类型,一般以“_info”结尾。可以说一个字节码文件就是一张大的表。
表结构
class文件是严格按照下表从上到下的顺序紧凑排列。
符号类型 | 名称 | 数量 | 描述 |
---|---|---|---|
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 | 属性变量 |
magic
魔法数,验证class文件的合法性(可以理解为识别码)。
major_version
主版本号,用于标识class文件的版本。java虚拟机只能运行低于自己的版本的class文件。
constant_pool
常量表主要用来存放两大类型常量:字面量和符号引用。
字面量: 文本字符串、被申明为了final的常量值等。
符号引用: 模块导出或开放的包、类或接口的全限定名、字段名称描述符、方法的名称和描述、方法句柄和方法类型、动态调用点和动态常量。
其实常量池中还细分为17种常量类型来存储,具体如下( 借用书上的一张图,读者了解即可)
access_flags
这是权限访问标识,比如记录一个对象是类还是接口,如果是类是否使用final、abstract、public、private修饰等。
this_class、super_class、interfaces
描述一个类的继承关系和接口实现情况在字节码文件中是通过这三个来描述的。 通过前面我们知道,一个类的基本信息,类名称、方法名称、字段信息等都放在常量池中,所以this_class、super_class和interfaces都是存放着常量池中的索引,表示为当前类全限定名索引、当前类的父类全限定名索引和当前类实现的所有接口的全限定名。
fileds
filed是一张字段表集合,存放描述该类中定义的所有静态变量和实例变量,但不包括方法内定义的局部变量。记录属性访问权限(default、public、private、protected),是实例变量还是静态变量(static),是否是常量(final),是否并发可见性(volatile),是否可被序列化(transient)以及字段的类型和名称。
methods
methods是一长方法表集合,虚拟机设计者们采用了和fields字段表集合几乎一致的方式来描述类中的方法,记录了方法的访问权限标识、是否是静态方法、是否是同步方法,是否是本地方法,名称索引和属性表集合索引(描述方法内部代码的属性表)等。
attributes
属性表是用于存放一些专有属性,比如方法表中代码编译为字节码指令,方法抛出的异常列表,一个匿名类或内部类的文件信息、源码和字节码指令的对应关系等,可以理解为其他项的私有仓库。并且虚拟机中对属性的定义很宽松,只要属性名不重复即可,所以我们可能通过编译器将一些个性化信息保存在其中。
到这里字节码文件内容就完成了。 虚拟机对字节码规范的篇幅很长,本文只是通过博主的理解简单介绍,希望对你有帮助。
总结
字节码技术是相对底层的技术,博主希望读者能通过本博文了解一些字节码的技术内幕。了解我们通常编写的java代码是如何在字节码中存储,给你以后学习java虚拟机类加载机制和内存模型奠定了基础。
参考文献《深入了解Java虚拟机》周志明 第3版
原创不易,转载请标明来源