当前位置: 首页 > news >正文

Java 字节码与 ASM 框架实战解析

Java 虚拟机(JVM)以字节码(Bytecode)为基础执行所有 Java 程序。对于希望深入理解 Java 底层运行机制,或开发自定义编译器、性能探测器、动态增强框架(如代理、AOP)的开发者来说,掌握 Java 字节码结构与 ASM 等字节码操作工具极为重要。

本篇文章将深入解析 Java 字节码的结构、工具链(如 javap)、以及如何通过 ASM 框架动态生成和修改字节码内容。


一、什么是字节码?

Java 源代码(.java 文件)经过 javac 编译后生成 .class 文件,包含了平台无关的字节码指令(Bytecode),供 JVM 执行。这种指令是一种中间语言,介于高级语言与机器语言之间。

例如:

 
public class Hello {public void say() {System.out.println("Hello World");} }

编译后,通过如下命令可以查看其字节码:

 
javap -c Hello.class

输出内容如下(部分):

 
public void say();Code:0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc #3 // String Hello World6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V9: return

这代表:

  • getstatic 从静态字段获取 System.out

  • ldc 加载常量 "Hello World"

  • invokevirtual 调用实例方法 println

  • return 返回。


二、字节码结构简要分析

Java 字节码文件由多个部分构成,主要包括:

部分 含义
Magic Number 文件标识(0xCAFEBABE)
Version 字节码版本(如 Java 8 是 52)
Constant Pool 常量池,存储字符串、类名等
Access Flags 类访问修饰符
Class Info 类名、父类名等
Interfaces 实现的接口
Fields 所有字段
Methods 所有方法及字节码
Attributes 方法、类的额外信息(如注解)

通过 javap -verbose 可以查看这些结构。


三、为什么需要 ASM?

Java 提供反射机制可动态访问类结构,但不能动态修改字节码。而 ASM 是一个轻量级、性能极高的字节码操作库,它允许你:

  • 生成 .class 文件;

  • 修改现有类的字节码;

  • 插入、替换方法;

  • 创建代理、日志增强、安全检查器等。

ASM 属于底层库(不像 Javassist 这样更面向语义),性能高、控制精确,是许多框架(如 Spring、MyBatis、ByteBuddy、Groovy)的基础组件。


四、ASM 基础使用:生成 Hello 类

1. 引入 Maven 依赖

 
<dependency><groupId>org.ow2.asm</groupId><artifactId>asm</artifactId><version>9.6</version> </dependency>

2. 生成 Hello 类(含 say 方法)

 
import org.objectweb.asm.*;import java.io.FileOutputStream;import static org.objectweb.asm.Opcodes.*;public class GenerateHelloClass {public static void main(String[] args) throws Exception {ClassWriter cw = new ClassWriter(0);cw.visit(V1_8, ACC_PUBLIC, "HelloGenerated", null, "java/lang/Object", null);// 构造函数MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);mv.visitCode();mv.visitVarInsn(ALOAD, 0); // thismv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);mv.visitInsn(RETURN);mv.visitMaxs(1, 1);mv.visitEnd();// say 方法mv = cw.visitMethod(ACC_PUBLIC, "say", "()V", null, null);mv.visitCode();mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");mv.visitLdcInsn("Hello ASM");mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);mv.visitInsn(RETURN);mv.visitMaxs(2, 1);mv.visitEnd();cw.visitEnd();byte[] bytes = cw.toByteArray();FileOutputStream fos = new FileOutputStream("HelloGenerated.class");fos.write(bytes);fos.close();} }

生成的 HelloGenerated.class 可直接用 java HelloGenerated 调用。


五、修改已有类:插入日志代码

假设我们想在任意方法前后插入日志输出:

 
System.out.println("Method start"); System.out.println("Method end");

我们可以通过 ClassReader + ClassWriter + MethodVisitor 实现:

 
ClassReader reader = new ClassReader("com.example.MyClass"); ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);ClassVisitor visitor = new ClassVisitor(ASM9, writer) {@Overridepublic MethodVisitor visitMethod(int access, String name, String descriptor,String signature, String[] exceptions) {MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);return new MethodVisitor(ASM9, mv) {@Overridepublic void visitCode() {mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");mv.visitLdcInsn(">> Entering method: " + name);mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println","(Ljava/lang/String;)V", false);super.visitCode();}@Overridepublic void visitInsn(int opcode) {if (opcode >= IRETURN && opcode <= RETURN) {mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");mv.visitLdcInsn("<< Exiting method: " + name);mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println","(Ljava/lang/String;)V", false);}super.visitInsn(opcode);}};} }; reader.accept(visitor, 0);

这段代码将在每个方法开始前和 return 前插入日志语句,非常适合开发调试增强类工具。


六、ASM 与 JavaAgent 动态增强结合

ASM 通常结合 Java Agent(Java 代理)用于运行时修改类结构,实现如:

  • 字节码级别的 AOP;

  • 自动性能采样工具(如 perf4j、Arthas);

  • 日志注入、安全增强、反作弊逻辑。

核心是通过 InstrumentationretransformClassesClassFileTransformer 机制将 ASM 插入类加载过程。


七、与 Javassist、ByteBuddy 比较

特性 ASM Javassist ByteBuddy
操作层级 字节码指令 Java 源码级 高层语义级
性能 非常高 中等 较高
易用性 较复杂 简单 简单
控制粒度 最高

结论:若你追求极致性能和精细控制,选择 ASM;若想快速实现功能,可用 Javassist 或 ByteBuddy。


八、结语

Java 字节码是理解 JVM 的关键,掌握 ASM 则是深入字节码世界的通行证。在工程实践中,通过 ASM:

  • 你可以实现字节码增强、性能插桩;

  • 你可以构建动态代理、AOP 框架;

  • 你甚至可以构建自己的语言或 DSL 编译器。

掌握了 ASM,不仅能写更高效的工具,还能读懂许多主流框架的底层实现原理。

相关文章:

Java 字节码与 ASM 框架实战解析

Java 虚拟机(JVM)以字节码(Bytecode)为基础执行所有 Java 程序。对于希望深入理解 Java 底层运行机制,或开发自定义编译器、性能探测器、动态增强框架(如代理、AOP)的开发者来说,掌握 Java 字节码结构与 ASM 等字节码操作工具极为重要。 本篇文章将深入解析 Java 字节码…...

计算机大数据毕业设计选题:基于Spark+hadoop的全球香水市场趋势分析系统 - 详解

计算机大数据毕业设计选题:基于Spark+hadoop的全球香水市场趋势分析系统 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "…...

Dos的常用命令

常用的Dos命令 #盘符切换 #查看当前目录下的所有文件 dir #切换目录 cd change directory cd..返回上一级 #清理屏幕 cls(clear screen) #退出终端 exit #查看电脑的ip ipconfig #打开应用calc(计算器)mspaint(画图)notepad(笔记)#ping 命令 ping www.baidu.com#文件操作…...

持续集成自动化CI/CD

背景Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。持续集成(Continuous integration)是一种软件开发实践,即团队开…...

Lightroom Classic 2025(LRC 2025)安装教程(附直接安装包下载)+入门操作指南

一、LRC 2025下载及软件介绍 Lightroom Classic 2025 v14.0.1 是 Adobe 旗下专业图片后期处理软件,主打RAW 格式图片编辑,专为摄影师、摄影爱好者及数码摄影 / 图形设计从业者设计,提供从照片导入、整理到编辑、打印的全套服务。支持各类相机 RAW 文件解析,能高效完成照片调…...

2025/09/14 【二叉树11】完全二叉树的节点个数

222. 完全二叉树的节点个数 - 力扣(LeetCode)...

8888

jjjj...

接口限流代码 - 实践

接口限流代码 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-size: 14p…...

OutGuess 安装与问题排查指南(Kali Linux 环境)

OutGuess 安装与问题排查指南(Kali Linux 环境) 本文档基于实际操作流程,整理了 OutGuess 工具从下载、编译安装到常见问题解决的完整步骤,适用于 CTF 隐写术场景下的工具配置需求。 一、工具简介 OutGuess 是经典隐写术工具,主要用于在 JPEG/PPM 图像 中隐藏 / 提取数据,…...

拓展操作码举例

拓展操作码举例 指令字长16位,每个地址码占4位: 那么如果要表示三地址操作码有16条 前四位表示操作码op,后面有3个4位长的地址字段A1,A2,A3 三地址指令 至少需要将1111留作扩展操作码之用,因此三地址指令只能有2^4 -1=15条 二地址指令 至少将 1111 1111 留作扩展操作码之…...

TryHackMe | Cicada-3301 Vol:1

TryHackMe | Cicada-3301 Vol:1 一、分析音频软件 先把文件下载到本地使用unzip解压缩,得到一个音频文件和一个图片我们这边先查看一下图片里面是什么内容大致意思:你好。我们正在寻找高智商的人。为了找到他们,我们设计了一个测试。 这张图片里藏有一条信息。找到它,它会指…...

[MCP][01]简介与概念

MCP简介与概念简介 MCP(全称为Model Context Protocol,模型上下文协议)是一种面向大模型交互过程的通用上下文协议标准。其核心目标在于为模型构建一个结构化、可控、可扩展的语义执行环境,使语言模型能够在统一的上下文管理体系下进行任务调度、工具调用、资源协作与状态保…...

完整教程:Word添加图/表题注

完整教程:Word添加图/表题注pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-s…...

CF819B Mister B and PR Shifts

CF819B Mister B and PR Shifts 题目描述 Some time ago Mister B detected a strange signal from the space, which he started to study. After some transformation the signal turned out to be a permutation $ p $ of length $ n $ or its cyclic shift. For the furthe…...

第一次自我介绍

大家好,我是李嘉倩,核心兴趣是《和平精英》《第五人格》这类竞技游戏,更擅长把游戏变成 “数据收集与分析” 的实践场,这也是我的核心优势。 为提升游戏策略,我坚持1 年半做了两件事:数据收集:用 Excel 记录《和平精英》200 + 局的落地点物资刷新率、对战淘汰 / 成盒原因…...

在Linux环境部署Flask应用并启用SSL/TLS安全协议

部署Flask应用通常涉及以下几个步骤:准备应用程序、选择合适的WSGI服务器、配置Web服务器代理、申请SSL证书以及配置SSL。 准备Flask应用 首先,你需要在Linux服务器上安装必要的软件,包括:Python和pip(Python包管理器) 虚拟环境(virtualenv或conda) Flask框架和依赖库使…...

0127_责任链模式(Chain of Responsibility)

责任链模式(Chain of Responsibility) 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。 UML 图优点降低耦合度:请求发送者不需要知道哪个对象处理请求 动态组合:可以…...

洛枫娜娜米讨厌数学……?

Xuan_qwq 好闪,拜谢 Xuan_qwq!插板 Catalan 数 应用一般有两种模型:折线型。 二叉树型。? CF1696E Placing Jinas 诈骗题。“最小值”纯诈骗,实际上操作次数和操作顺序没半毛钱关系。 每个点上的操作次数就是从 $(0, 0)$ 到这个点的路径条数,这是容易理解的,一条路径带来…...

Spatial 语言核心概念简介

我们来详细解析一下斯坦福大学的 Spatial 语言。这是一个非常专业且强大的领域特定语言(DSL),用于高性能硬件(如FPGA或ASIC)生成。 一、Spatial 语言核心概念简介 Spatial 是一个嵌入在 Scala 内部的 DSL(Domain-Specific Language)。它的根本目标是让软件工程师和算法专…...

Redis数据库的五类核心数据结构

Redis是一个开源的键值存储系统,支持多种类型的值模型,其中五种核心的数据结构是:String(字符串)、List(列表)、Set(集合)、Hash(哈希表)、和Sorted Set(有序集合)。下面将对每种数据结构进行详细解析。 1. String(字符串) String数据结构是最简单的数据类型,通…...

RAG 个人知识库 向量查找原理

...

css-1

css的三大特性继承性:子元素继承父元素样式主要是跟文字相关的样式继承。层叠性:后面样式会覆盖前面样式,主要解决样式冲突问题。但是要看选择器权重来确定优先级优先级:优先级由选择器的 权重 决定,权重高的规则覆盖权重低的规则css的优先级原则:1. 优先级相等的时候,CS…...

Java-JDK8新特性

Java-JDK8新特性主要讲解: 1.Java8概述 2.Lambda表达式 3.函数式接口 4.方法引用 5.Stream API 6.新时间API 1.Java8概述 略 2.Lambda表达式 https://github.com/godmaybelieve...

解决MySQL ONLY_FULL_GROUP_BY 错误的方案

在MySQL数据库中,ONLY_FULL_GROUP_BY是一个服务器SQL模式,它用于控制对 GROUP BY子句的处理。如果启用了 ONLY_FULL_GROUP_BY,任何不在 GROUP BY子句中也没有使用聚合函数的非聚合列都会导致错误。这是为了防止不确定的结果,因为当选择非聚合列而没有明确指定如何对这些列进…...

博客园美化

参考: 基本模板:博客园美化(最全) 看板猫:博客园看板猫 大部分套用的是模板,自己更改了一些尺寸、位置、透明度之类的。下面放上博客定制css。 #google_ad_c1, #google_ad_c2 {display:none;} .syntaxhighlighter a, .syntaxhighlighter div, .syntaxhighlighter code, .…...

spatial 一个芯片设计语言的简介 scala dsl 并行支持 -1

以下是对原网站内容的翻译 https://spatial-lang.readthedocs.io/en/legacy/tutorial/starting.html 以下是您提供的Spatial框架入门指南的中英对照翻译:​​0. Getting Started | 0. 开始使用​​ ​​Prerequisites | 先决条件​​ First, make sure to download and instal…...

NOIP备考

模拟赛 1 T1 https://www.luogu.com.cn/problem/T664700 前置知识是 P5019。 很典的思路。在 \(a\) 序列前后都塞 \(a_0=a_{n+1}=0\)。算长 \(n+2-1=n+1\) 的差分数组 \(c\)。易知 \(a\) 是 \(c\) 的一个前缀和数组,即 \(a_i+c_i=a_{i+1}\)。已知 \(a_0=0\),只需要差分数组都…...

NVIDIA GPGPU 访存通路设计调研

纵向结构上,传统架构仅对用户暴露 2 层存储交互,而随着 Hopper 添加 st.async ,NVIDIA GPU 完成暴露 3 层存储结构的双向通信接口,即 \(2\times C_{3}^{2}=6\) 一共 6 种指令。Src\Dst RF SMEM DRAMRF x st. Shared st. GlobalSMEM ld. Shared x st. Async (Hopper)DRAM ld…...

用 Java 和 Tesseract 实现验证码图像识别

验证码图像识别在自动化测试、信息采集、系统登录等场景中有着重要的应用价值。本文将介绍如何使用 Java 语言结合 Tesseract OCR 引擎,构建一个完整的验证码图像识别流程,包括图像预处理与识别优化。 一、环境准备 安装 Java(推荐版本 11 及以上) 安装 Tesseract OCR 引擎…...

AGC003D

题意是给定一个集合 \(S\) 要求找到它的最大的子集使得子集里的任意两个数相乘都不是完全立方数。 \(S_i\le1e10\),集合大小小于 \(1e5\)。 首先对于每个数都把它的因子的指数对 \(3\) 取模,然后可以发现操作完了的形式都只有一种形式与它相乘可以得到完全立方数的数。那就在…...

Java 实现验证码图像识别与处理流程详解

在实际开发中,自动化处理验证码图像是提升系统智能化和测试效率的一个关键点。Java 作为一门稳健的编程语言,结合 OCR 技术可以有效实现验证码识别。本文将介绍如何使用 Java 配合 Tesseract OCR 引擎完成从图像读取、预处理到文字识别的完整流程。 一、项目依赖准备 安装 Ja…...

图论杂题。

胡马渐远蹄声尽,四顾萧条暮色起。 空城角随西风吟,废池乔木,犹厌言兵。 ——《无题》luogu P6880 反转边等价于删一条再加一条边。 加边的肯定随便求。 删边,如果删在最短路上我们就暴力跑一遍;否则肯定还是最短路。两个方向最短路上 \(\mathcal{O}(n)\) 条边。用稠密图朴…...

暑假训练小结

主要做bzoj题单。 前几天相当痛苦,水平太菜题单根本做不下去。 基本都是跟着题解写代码。 还记得最开始写的是一道网络流然后学的是ek。 熬过第一个阶段之后,从杀蚂蚁那道题之后开始发现自己可以大概看懂大部分题解的思路了。 别问我为什么是杀蚂蚁,因为那段时间里这个记得最…...

初识python:一些基础的知识(函数)

目录函数函数的几种定义方法函数的返回值函数的调用函数的实参和形参实参的分类 函数 函数的几种定义方法 函数拥有以下几种定义方法,第一种:没有参数 def self_max(): a,b = 10,20 if a > b: print(a) elif a == b: print(别搞,两个变量相同。) else: print(b) self_max(…...

Java并发编程(3)

Java内存模型 1、说一下你对Java内存模型(JMM)的理解Java程序运行在各种硬件和操作系统上,不同硬件的CPU缓存策略、内存访问顺序、指令重排规则可能都不一样。那JMM是Java规范定义的一个抽象模型,是一套规则:线程和主内存的交互:线程如何从主内存读变量、写变量 可见性保…...

斐波那契子序列

到处乱逛找到的一道有意思的题。 定义斐波那契序列为:前两项值不做限制,\(f_i=f_{i-1}+f_{i-2}(2<i\le n)\)。 给定一个长度为 \(n\) 的序列 \(a\),找出其最长的斐波那契子序列。 如果有多个最长输出字典序最小的一个。 正解做法貌似为 \(n^2logn\)。即动态规划加二分。 …...

[豪の学习笔记] 软考中级备考 基础复习#10

UML建模概述、类图、用例图、顺序图、活动图、状态图、通信图、构件图跟学视频:学以致知Learning - 软件设计师 基础阶段|考点理论精讲 Chapter 10 - UML建模 1 - 概述 ​ 统一建模语言UML是面向对象软件的标准化建模语言。UML由三个要素构成:UML的基本构造块、支配这些构造块…...

题解:CF2137D Replace with Occurrences

题意为给定一个长度为 \(n\) 的序列 \(b\),要求你构造一个序列 \(a\) 使得对于每一个序列 \(a\) 中的数 \(a_i\),在序列 \(a\) 都出现了 \(b_i\) 次。 可以发现 \(a\) 序列中的数的大小是无关紧要的,重要的是出现次数。 一开始可以很快的得出一个错解那就是判断完有无解之后…...

题解:CF2137C Maximum Even Sum

题意是给定两个数 \(a,b\),你可以进行一次操作,选定一个 \(b\) 的因数 \(k\),将 \(a\) 变为 \(a \times k\),并将 \(b\) 变为 \(b/k\),求出如何操作可以使得 \(a+b\) 是一个偶数,并且值最大,请输出这个最大值。 如果不考虑 \(a+b\) 是否为偶数,容易想到最大值为 \(a\ti…...

第02周 java预习

课前问题列表 1.方法相关问题 public class Main {static void changeStr(String x) {x = "xyz";}static void changeArr(String[] strs) {for (int i = 0; i < strs.length; i++) {strs[i] = strs[i]+""+i;}}public static void main(String[] args) {…...

编码规范

1.不对指针变量进行sizeof操作。 2.数组作为函数参数时,必须同时将其长度作为函数的参数。 3.字符串或指针作为函数参数时,请检查参数是否为NULL. 4.对字符串进行存储操作,确保字符串有\0结束符。 5.整数之间运算时必须严格检查,确保不会出现溢出、符号反转或除以0。 6.内存…...

深入解析:【译】Visual Studio 八月更新已发布 —— 更智能的人工智能、更出色的调试功能以及更多控制权

深入解析:【译】Visual Studio 八月更新已发布 —— 更智能的人工智能、更出色的调试功能以及更多控制权pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &q…...

命令模式在 TPL Dataflow 反馈回路管道中的应用及问题解决

命令模式与 TPL Dataflow 基础概念 命令模式的核心在于对一类对象公共操作的抽象,通过定义统一的方法签名,将操作封装为独立对象。这种设计思路不仅实现了调用者与实现者的解耦,还为事件驱动架构和 AOP 思想提供了天然支持。在.NET 生态中,TPL Dataflow 组件为这种抽象提供…...

Ubuntu 24.04 服务器调整MySQL 8.0.42 三节点集群(一主两从架构)安装部署配置教程

Ubuntu 24.04 服务器调整MySQL 8.0.42 三节点集群(一主两从架构)安装部署配置教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &q…...

使用almalinux基础镜像创建nginx镜像

前言 使用almalinux:latest镜像构建nginx镜像,启动容器时,nginx自动启动 创建基本文件 cd ~ mkdir nginxtest cd nginxtest/ echo nginx s running!!! >> index.html vi Dockerfile编辑dockerfile文件内容 FROM almalinux:latest MAINTAINER "smartaiops<smar…...

docke容器版Nessus登录+破解+激活+特征库更新

docke容器版Nessus登录+破解+激活+特征库更新 安装Nessus docker run -itd --name=ramisec_nessus -p 8834:8834 ramisec/nessus 重置账号密码1. 进入容器 docker exec -it ramisec_nessus bash2. 查看用户 /opt/nessus/sbin/nessuscli lsuser3. 修改指定用户的密码(以admin为…...

我把Cursor当磁盘清理工具用,非常棒! - ukyo-

我来帮您删除这个Colima虚拟磁盘镜像文件: [2 tools called] ✅ **文件删除成功!** **文件信息**:- **文件名**: basedisk- **位置**: ~/.colima/_lima/colima/- **大小**: 456MB- **类型**: Colima 虚拟磁盘镜像文件 **释放的磁盘空间**: **456MB** **删除后的影响**:- Co…...

vue项目

新建文件夹,然后用vscode打开这个文件然后在终端新建vue文件...

第九篇:数据库服务克隆应用

数据库克隆概念介绍 在数据库MySQL 8.0(8.0.17+)版本中,引入了数据库的克隆功能,主要是借助clone-plugin实现的,是对数据页底层克隆; 克隆的数据是InnoDB存储引擎中的物理快照信息,包括schemas, tables, tablespaces, and data dictionary metadata; 在数据库中出现克隆…...

Anti-Proxy Attendance 题解

CF1924F 题解题目传送门:CF1924F 还是第一次见这种势能题。 先把交互库的回答转成 \(0,1\) 表示答案是否在这个区间中。 首先把题目转化一下,对每个位置 \(i\) 维护一个 01 串 \(S_i\) 表示:如果 \(i\) 是答案,那么当前交互库的每个回答是否是真话。即如果当前询问 \([l,r]…...