同步操作将从 turnon/blog 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
title: Java 字节码
date: 2019-10-28 22:04:39
categories:
- Java
- JavaSE
- JVM
tags:
- Java
- JavaSE
- JVM
- 字节码
permalink: /pages/e9eb4b/
Java 字节码是Java虚拟机执行的一种指令格式。之所以被称之为字节码,是因为:Java 字节码文件(.class
)是一种以 8 位字节为基础单位的二进制流文件,各个数据项严格按照顺序紧凑地排列在 .class 文件中,中间没有添加任何分隔符。整个 .class 文件本质上就是一张表。
Java 能做到 “一次编译,到处运行”,一是因为 JVM 针对各种操作系统、平台都进行了定制;二是因为无论在什么平台,都可以编译生成固定格式的Java 字节码文件(.class
)。
一个 Java 类编译后生成的 .class 文件内容如下图所示,是一堆十六进制数。
图来自 字节码增强技术探索
字节码看似杂乱无序,实际上是由严格的格式要求组成的。
每个 .class
文件的头 4 个字节称为 魔数(magic_number)
,它的唯一作用是确定这个文件是否为一个能被虚拟机接收的 .class
文件。魔数的固定值为:0xCAFEBABE
。
版本号(version)有 4 个字节,前两个字节表示次版本号(Minor Version),后两个字节表示主版本号(Major Version)。
举例来说,如果版本号为:“00 00 00 34”。那么,次版本号转化为十进制为 0,主版本号转化为十进制为 52,在 Oracle 官网中查询序号 52 对应的主版本号为 1.8,所以编译该文件的 Java 版本号为 1.8.0。
紧接着主版本号之后的字节为常量池(constant_pool),常量池可以理解为 .class 文件中的资源仓库。
常量池整体上分为两部分:常量池计数器以及常量池数据区
常量池计数器(constant_pool_count) - 由于常量的数量不固定,所以需要先放置两个字节来表示常量池容量计数值。
常量池数据区 - 数据区的每一项常量都是一个表,且结构各不相同。
常量池主要存放两类常量:
final
的常量值。紧接着常量池的 2 个字节代表访问标志(access_flags),这个标志用于识别一些类或者接口的访问信息,描述该 Class 是类还是接口,以及是否被 public
、abstract
、final
等修饰符修饰。
类索引(this_class)和父类索引都是一个 u2 类型的数据,而接口索引集合是一组 u2 类型的数据的集合。.class 文件中由这 3 项数据来确定这个类的继承关系。
字段表用于描述类和接口中声明的变量。包含类级变量以及实例级变量,但是不包含方法内部声明的局部变量。
字段表也分为两部分,第一部分为两个字节,描述字段个数;第二部分是每个字段的详细信息 fields_info。
字段表结束后为方法表,方法表的结构如同字段表一样,依次包括了访问标志、名称索引、描述符索引、属性表集合几项。
属性表集合存放了在该文件中类或接口所定义属性的基本信息。
字节码指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零到多个代表此操作所需参数(Operands)而构成。由于 JVM 采用面向操作数栈架构而不是寄存器架构,所以大多数的指令都不包括操作数,只有一个操作码。
JVM 操作码的长度为 1 个字节,因此指令集的操作码最多只有 256 个。
字节码操作大致分为 9 类:
对于需要手动操纵字节码的需求,可以使用 Asm,它可以直接生产 .class
字节码文件,也可以在类被加载入 JVM 之前动态修改类行为(如下图 17 所示)。
Asm 的应用场景有 AOP(Cglib 就是基于 Asm)、热部署、修改其他 jar 包中的类等。当然,涉及到如此底层的步骤,实现起来也比较麻烦。
Asm 有两类 API:核心 API 和树形 API
Asm Core API 可以类比解析 XML 文件中的 SAX 方式,不需要把这个类的整个结构读取进来,就可以用流式的方法来处理字节码文件。好处是非常节约内存,但是编程难度较大。然而出于性能考虑,一般情况下编程都使用 Core API。在 Core API 中有以下几个关键类:
Asm Tree API 可以类比解析 XML 文件中的 DOM 方式,把整个类的结构读取到内存中,缺点是消耗内存多,但是编程比较简单。TreeApi 不同于 CoreAPI,TreeAPI 通过各种 Node 类来映射字节码的各个区域,类比 DOM 节点,就可以很好地理解这种编程方式。
利用 Javassist 实现字节码增强时,可以无须关注字节码刻板的结构,其优点就在于编程简单。直接使用 java 编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构或者动态生成类。
其中最重要的是 ClassPool、CtClass、CtMethod、CtField 这四个类:
CtClass(compile-time class)
- 编译时类信息,它是一个 class 文件在代码中的抽象表现形式,可以通过一个类的全限定名来获取一个 CtClass 对象,用来表示这个类文件。ClassPool
- 从开发视角来看,ClassPool 是一张保存 CtClass 信息的 HashTable,key 为类名,value 为类名对应的 CtClass 对象。当我们需要对某个类进行修改时,就是通过 pool.getCtClass("className")方法从 pool 中获取到相应的 CtClass。CtMethod
、CtField
- 这两个比较好理解,对应的是类中的方法和属性。jclasslib - IDEA 插件,可以直观查看当前字节码文件的类信息、常量池、方法区等信息。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。