参考链接:美团技术团队
使用javap -verbose命令分析一个字节码文件时,将会分析该字节码文件的魔术、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息。
字节码.png
java字节码整体结构
image.png魔术
- 所有的.class文件的前四个字节都是魔数,魔数的固定值为:0xCAFEBABE。魔数放在文件开头,JVM可以根据文件的开头来判断这个文件是否可能是一个.class文件,如果是,才会继续进行之后的操作。
版本号
- 版本号为魔数之后的4个字节,前两个字节表示次版本号(Minor Version),后两个字节表示主版本号(Major Version)。上图2中版本号为“00 00 00 34”,次版本号转化为十进制为0,主版本号转化为十进制为52,在Oracle官网中查询序号52对应的主版本号为1.8,所以编译该文件的Java版本号为1.8.0。
常量池
- 紧接着主版本号之后的字节为常量池入口。一个java类中定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是Class文件的资源仓库,比如Java类中定义的方法与变量信息,都是存储在常量池中。常量池中存储两类常量:字面量与符号引用。
- 字面量:代码中声明为Final的常量值。
- 符合引用:符号引用如类和接口的全局限定名、字段的名称和描述符、方法的名称和描述符。
-
常量池总体结构:Java常量池整体上分为两部分:常量池数量(常量池计数器)与常量池数组(常量池数据区)构成。常量池数量紧跟在主版本号后面,占据2个字节;常量池数组则紧跟在常量池数组之后。常量池数组中元素的类型和结构是不同的。每一种元素的第一个数据都是一个u1类型,该字节是一个标志位,占据1个字节。JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型。常量池数组中元素的个数= 常量池数 - 1,目的是满足某些常量池索引值的数据在特定情况下需要表达【不引用任何一个常量池】的含义:根本原因在于,索引为0也是一个常量,只不过它不在常量表中,这个常量就对应null值,所有,常量池的所有从1开始而不是0。
常量池结构.png -
在字节码中共有14种类型的cp_info(如下图6所示),每种类型的结构都是固定的
image.png - 在JVM规范中,每个变量/字段都有描述信息,描述信息主要的作用是描述字段的数据类型、方法的参数列表(包括数量、类型与顺序)与返回值。根据描述规则,基本数据类型和代表无返回值的void类型都用一个大写字母表示,对象类型则使用字符L加对象的全限定名称来表示。为了压缩字节码文件的体积,对于基本数据类型,JVM都只使用一个大写字母来表示,如下所示: B--byte、C--char、D--double、F--Float、I--int、J--long、S--short、Z--boolean、V--void、 L--对象类型,如 Ljava/lang/String; 。
- 对于数组类型来说,每一个维度使用一个前置的[来表示,如int[]被记录为[I,Stirng[][]被记录为[[Ljava/lang/String; 。
- 用描述符描述方法,按照先参数列表,后返回值得顺序来描述,参数列表按照参数的严格顺序放在一组()之内,如方法: String getCardByIdAndName(int id,String name)的描述符为: (I,Ljava/lang/String;)Ljava/lang/String; 。
访问标志
描述该Class是类还是接口,以及是否被Public、Abstract、Final等修饰符修饰。
访问标志.png
类索引、父类索引、接口索引
略
字段表
用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量。但是不包括方法内部声明的局部变量。字段表也分为两部分,第一部分为两个字节,描述字段个数;第二部分是每个字段的详细信息fields_info。字段表结构如下图所示:
字段表.png
-
fidles_info结构
fidles_info.png
方法表
字段表结束后为方法表,方法表也是由两部分组成,第一部分为两个字节描述方法的个数;第二部分为每个方法的详细信息。方法的详细信息较为复杂,包括方法的访问标志、方法名、方法的描述符以及方法的属性,如下图所示:
方法表.png
-
method_info结构
method_info.png -
attribute_info结构
attribute_info.png
Code结构
Code attribute的作用是保存该方法的结构,如下图所示:
Code attribute.png
- attribute_length表示attribute锁包含的自己数,不包含attribute_name_index和attribute_length字段。
- max_stack表示这个方法运行的任何时刻所能达到的操作数栈的最大深度。
- max_loacls表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。
- code_length表示该方法锁包含的字节码的字节数以及具体的指令码。 具体字节码即是该方法被调用时,虚拟机锁执行的字节码。
- exception_talbe这里存放的是处理异常的信息。每个eception_table表项由start_pc,end_pc,handler_pc,catch_type组成。
- start_pc和end_pc表示在code数组中的从start_pc到end_pc处(包含start_pc,不包含end_pc)的指令抛出的异常会由这个表项来处理。
- handler_pc表示处理异常的代码的开始处。
- catch_type表示会被处理的异常类型,它指向常量池里的一个异常类。当catch_type为0时,表示处理所有的异常。
网友评论