浅析Java字节码文件

1.概要

java程序是虚拟机上运行,而虚拟机JVM实际运行的是字节码文件,且此字节码文件的源文件不一定是Java源代码,只要编译后的字节码文件符合Java虚拟机的规范即可。
字节码文件没有任何分隔符分隔区段,每个组成项(item)只用两个字节(0-65536)的无符号数(u2)标识此项的在字节码文件中所占用的字节数,各组成项(item)以Big-Endian顺序(大端,即按高位字节在地址最低位,最低字节在地址最高位来存储数据)连续存放,当超出这个字节数长度便是另外一项(item)内容,所以其内部结构是相当紧凑的。
每一项(item)都包括类型、名称、及其数量。类型可以是表名,也可以“基本类型”(即无符号数),字节码文件只有无符号数、表(Table)这两种数据结构。无符号数有u1 、u2、 u4、u8这四种,而表则是由无符号数及其他表组成的复合数据类型,以“_info”后缀结尾,整个字节码文件也可看成一张(很大的)表。

(1)基本类型(无符号数)

类型说明
u11字节的无符号数
u22字节的无符号数
u44字节的无符号数
u88字节的无符号数

(2)字节码文件结构

ClassFile {
// 数据类型			数据项
	u4				 magic;
	u2				 minor_version;
	u2				 major_version;
	u2				 constant_pool_count;
	cp_info 		constant_pool[constant_pool_count-1];
	u2	 			access_flags;
	u2				 this_class;
	u2				 super_class;
	u2				 interfaces_count;
	u2				 interfaces[interfaces_count];
	u2				 fields_count;
	field_info	 	fields[fields_count];
	u2				 methods_count;
	method_info 	methods[methods_count];
	u2	 			attributes_count;
	attribute_info	 attributes[attributes_count];
}

2.字节码文件各组成项(item)含义分析

(1)magic(魔数)

magic,魔数是字节码文件开始的4个字节,魔数值固定为 0xCAFEBABE,不会改变。魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的 Class 文件,如果开头不是0xCAFEBABE,JVM将认为该文件不是.class字节码文件而拒绝解析。

(2)major_version 、minor_version(版本号)

major_version 、minor_version,分别是主版本号、次版本号,它们在magic之后,各占两个字节。随着Java更新换代,不断扩充新特性,字节码文件的格式也随着变化。字节码文件的版本号确定了特定的字节码文件格式,只有给定了版本号JVM才能正确读取字节码文件。如果其版本号超出了JVM能处理的有效范围,JVM则不会处理加载些字节码文件。如高版本JDK编译的class文件不能在低版本的JVM中运行,而高版本的JVM却能向下兼容运行低版本JDK编译的class文件。

(3)constant_pool_count(常量池计数器)与constant_pool[](常量池)

  • constant_pool_count
    常量池计数器 的计数值从1开始,因此计数器值等于 constant_pool 表中的成员数加 1。虽然常量计数器并没有将0作为计数值,但索引0却还是存在的,主要是为了满足后续其他项在不引用常量池中的任何常量时,默认可以所常量池中的访问索引设为0来表示。
  • constant_pool[]
    常量池是字节码文件中非常重要的数据项,与其他项关联最多和占用空间最大的数据项。常量池主要存放字面量(Literal)和符号引用(Symbolic references)两类数据常量,其他项以索引的方式来访问常量池。
    • 常量池基本结构
      常量池数组紧跟常量池计数器之后,与一般数组概念不同,常量池数组中不同元素的类型、结构都是不同的,长度也不同,但每一元素的第一个数据tag都是一个u1类型,此字节是个标志位,根据tag的值确定该元素的具体类型。

1)常量池分布

常量池结构

2)常量池组成元素

不同元素之间,其结构与类型是不同的,JVM一共定义了11种常量,并针对不同的常量进行专门的解析读取。

常量池元素结构表
类型/含义结构类型说明
CONSTANT_Utf8_info
UTF-8编码的字符串
tag  u1   值为1
lengthu2UTF-8缩略编码字符串所占用的字节数
bytesu1长度为length的UTF-8码字符串
CONSTANT_Integer_info
整型字面量
tagu1值为3
bytesu4按照高位在前(低地址位置)储存int值
CONSTANT_Float_info
单精度浮点型字面量
tagu1值为4
bytesu4按照高位在前(低地址位置)储存float值
CONSTANT_Long_info
长整型字面量
tagu1值为5
bytesu8按照高位在前(低地址位置)储存long值
CONSTANT_Double_info
双精度浮点型字面量
tagu1值为6
bytesu8按照高位在前(低地址位置)储存double值
CONSTANT_Class_info
类或接口的符号引用
tagu1值为7
indexu2指向全限定名常量项的索引
CONSTANT_String_info
字符串类型字面量
tagu1值为8
indexu2指向字符串字面量的索引
CONSTANT_Fieldref_info
字段的符号引用
tagu1值为9
indexu2指向声明字段的类或接口描述符CONSTANT_Class_info的索引
indexu2指向字段描述符CONSTANT_NameAndType_info的索引
CONSTANT_Methodref_info
类中方法的符号引用
tagu1值为10
indexu2指向声明方法的类描述符CONSTANT_Class_info的索引
indexu2指向方法名及类型描述符CONSTANT_NameAndType_info的索引
CONSTANT_InterfaceMethodref_info
接口中方法的符号引用
tagu1值为11
indexu2指向声明方法的接口描述符CONSTANT_Class_info的索引
indexu2指向方法名及类型描述符CONSTANT_NameAndType_info的索引
CONSTANT_NameAndType_info
字段或方法的部分符号引用
tagu1值为12
indexu2指向该字段名或方法名常量项的索引
indexu2指向该字段或方法描述符常量项的索引

(4)access_flags (访问标识)

access_flags,访问标识是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。

  • 访问和修饰符
标记名含义
ACC_FINAL0x0010不允许有子类
ACC_PUBLIC0x0001可以被包的类外访问。
ACC_SUPER0x0020当用到 invokespecial 指令时,需要特殊处理的父类方法。
ACC_INTERFACE0x0200标识定义的是接口而不是类。
ACC_ABSTRACT0x0400不能被实例化。
ACC_ANNOTATION0x2000标识注解类型
ACC_ENUM0x4000标识枚举类型

(5)this_class (类索引)

this_class,类索引的值必须是对 constant_pool 表中项目的一个有效索引值。constant_pool 表在这个索引处的项必须CONSTANT_Class_info 类型常量,表示这个 Class 文件所定义的类或接口。

(6)super_class (父类索引)


  • 对于类来说,super_class父类索引必须是对是onstant_pool 表中项目的一个有效索引值(Object除外,它没有父类,父类索引为0)。 constant_pool 表在这个索引处的项必须为 CONSTANT_Class_info 类型常量,表示这个 Class 文件所定义的类的直接父类。当前类的直接父类,以及它所有间接父类的 access_flag 中都不能带有 ACC_FINAL 标记。
  • 接口
    对于接口来说,super_class父类索引项的值必须是对 constant_pool 表中项目的一个有效索引值。constant_pool 表在这个索引处的项必须为代表 java.lang.Object 的 CONSTANT_Class_info 类型常量。

(7)interfaces_count (接口计数器)与interfaces[](接口表)

  • interfaces_count
    接口计数器的值表示当前类或接口的直接父接口数量。
  • interfaces[]
    接口表的每个成员的值必须是一个对 constant_pool 表中项目的一个有效索引值,它的长度为 interfaces_count。每个成员interfaces[i] 必须为 CONSTANT_Class_info 类型常量。成员所表示的接口顺序和对应的源
    代码中给定的接口顺序(从左至右)一样.

(8)fields_count(字段计数器)与fields[](字段表)

  • fields_count
    字段计数器,表示当前 Class 文件 fields[]数组的成员个数。
  • fields[]
    字段表,fields[]数组中的每个成员都必须是一个 fields_info 结构的数据项,用于表示当前类或接口中某个字段的完整描述。fields[]数组描述当前类或接口声明的所有字段,但不包括从父类或父接口继承的部分。

(9)methods_count(方法计数器)与methods[](方法表)

  • methods_count
    方法计数器,表示当前 Class 文件 methods[]数组的成员个数。
  • methods[]
    方法表,methods[]数组中的每个成员都必须是一个 method_info 结构的数据项,用于表示当前类或接口中某个方法的完整描述。如果某个 method_info 结构的access_flags 项既没有设置 ACC_NATIVE 标志也没有设置 ACC_ABSTRACT 标志,那么它所对应的方法体就应当可以被 Java 虚拟机直接从当前类加载,而不需要引用其它类。method_info 结构可以表示类和接口中定义的所有方法,包括实例方法、类方法、实例初始化方法和类或接口初始化方法。methods[]数组只描述当前类或接口中声明的方法,不包括从父类或父接口继承的方法。

(10)attributes_count(属性计数器)与 attributes[](属性表)

  • attributes_count
    属性计数器,表示当前 Class 文件 attributes 表的成员个数。
  • attributes[]
    属性表,attributes[] 表的每个项的值必须是 attribute_info 结构。Class 文件结构中的 attributes 表的项包括下列定义的部分或全部属性:InnerClasses、EnclosingMethod、Synthetic、Signature、SourceFile,SourceDebugExtension、Deprecated、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations以及BootstrapMethods属性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值