深入拆解 Java 虚拟机
郑雨迪
Oracle 高级研究员,计算机博士
87446 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
模块四:黑科技 (3讲)
深入拆解 Java 虚拟机
15
15
1.0x
00:00/00:00
登录|注册

33 | Java Agent与字节码注入

思考如何注入方法出口,包括异常执行路径
基于字节码注入的profiler的观察者效应
Java agent的类加载拦截功能
字节码注入作为AOP的一种实现方式
定义切入点和通知
利用字节码注入实现各式各样的profiler
利用字节码注入实现代码覆盖工具
观察者效应
ASM库的使用
ClassFileTransformer接口
Java虚拟机不限制Java agent的数量
agentmain方法
Attach API远程加载
premain方法
总结与实践
面向方面编程
基于字节码注入的profiler
字节码注入
Java Agent
Java Agent与字节码注入

该思维导图由 AI 生成,仅供参考

关于 Java agent,大家可能都听过大名鼎鼎的premain方法。顾名思义,这个方法指的就是在main方法之前执行的方法。
package org.example;
public class MyAgent {
public static void premain(String args) {
System.out.println("premain");
}
}
我在上面这段代码中定义了一个premain方法。这里需要注意的是,Java 虚拟机所能识别的premain方法接收的是字符串类型的参数,而并非类似于main方法的字符串数组。
为了能够以 Java agent 的方式运行该premain方法,我们需要将其打包成 jar 包,并在其中的 MANIFEST.MF 配置文件中,指定所谓的Premain-class。具体的命令如下所示:
# 注意第一条命令会向manifest.txt文件写入两行数据,其中包括一行空行
$ echo 'Premain-Class: org.example.MyAgent
' > manifest.txt
$ jar cvmf manifest.txt myagent.jar org/
$ java -javaagent:myagent.jar HelloWorld
premain
Hello, World
除了在命令行中指定 Java agent 之外,我们还可以通过 Attach API 远程加载。具体用法如下面的代码所示:
import java.io.IOException;
import com.sun.tools.attach.*;
public class AttachTest {
public static void main(String[] args)
throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException {
if (args.length <= 1) {
System.out.println("Usage: java AttachTest <PID> /PATH/TO/AGENT.jar");
return;
}
VirtualMachine vm = VirtualMachine.attach(args[0]);
vm.loadAgent(args[1]);
}
}
使用 Attach API 远程加载的 Java agent 不会再先于main方法执行,这取决于另一虚拟机调用 Attach API 的时机。并且,它运行的也不再是premain方法,而是名为agentmain的方法。
public class MyAgent {
public static void agentmain(String args) {
System.out.println("agentmain");
}
}
相应的,我们需要更新 jar 包中的 manifest 文件,使其包含Agent-Class的配置,例如Agent-Class: org.example.MyAgent
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了Java Agent与字节码注入的高级技术手段。首先介绍了Java agent的基本概念和使用方法,包括premain方法和Attach API远程加载的方式。然后详细介绍了Java agent的instrumentation机制,通过该机制可以拦截类加载事件并修改类的字节码,实现字节码注入。通过示例代码演示了如何使用ASM框架进行字节码注入,以及如何使用redefine和retransform功能对已加载的类进行修改。文章还提到了Java agent的功能是通过JVMTI agent实现的,通过注册钩子方法来实现对Java虚拟机事件的拦截和处理。另外,还介绍了基于字节码注入的profiler和面向方面编程(AOP)的相关概念。最后,提到了基于字节码注入的profiler所收集到的数据并不能反映程序的真实运行状态,而是程序在被注入的情况下的执行状态。整篇文章内容涵盖了Java agent的基本概念、使用方法和高级功能,对于想要深入了解Java agent和字节码注入的读者来说,是一篇非常有价值的文章。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Java 虚拟机》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(12)

  • 最新
  • 精选
  • 蚂蚁内推+v
    用attach的方式注入字节码的时候遇到了99线升高的性能问题,看一些资料说 class redefinition 的时候会阻塞线程。请问能详细讲下吗?

    作者回复: 你是在做redefine时出问题,还是没触发时便已出问题? class redefinition需要爬每个线程的Java栈,检查有没有用到被redefine的类的方法,因此会stop-the-world。另外,redefine后,JIT’ed代码会被抛弃,重新解释执行

    2018-11-19
    12
  • Scott
    我看到了jvmti可以回调异常事件,但是java.lang.instrument包下没有处理这个事件的,只能在load时回调,处理异常究竟是怎么做的?

    作者回复: Instrumentation包并没有所有JVMTI功能

    2018-10-06
    4
  • Scott
    出方法时需要注入的字节码除了返回,还有几种情况,如果没有catch块,就拦截throw,如果有,但是catch块里面可能有很多层,只是遍历inst应该是不可以的

    作者回复: 其实不用管有没有catch块,有没有throw,直接给所有代码罩一个catch any的异常处理就行了

    2018-10-06
    4
  • 阅过留痕 1:Java agent 是啥玩意? 这个概念老师没有详细讲解,我的理解是Java语言的一个特性,这个特性能够实现Java字节码的注入 2:Java字节码的注入有什么用处呢? 在平时编程几乎没有使用到这方面的功能,应该是在一些框架的设计的时候才使用吧!比如:专栏中提到的面相切面编程。 3:Java agent 本质上是通过 c agent 来实现的,那 c agent 本质上是怎么实现的呢? C agent是一个事件驱动的工具实现接口,通常我们会在 C agent 加载后的入口方案 Agent_OnLoad处注册各个事件的钩子方法。当Java虚拟机触发了这些事件时,便会调用对应的钩子方法 4:留个话头 写代码实现某些功能,我的理解有三个时间段 第一个:源码阶段,最常用的,也是编程的主要活动时间 第二个:字节码阶段,有些功能可能会在加载字节码时修改或者添加某些字节码,某些框架做的事情 第三个:运行阶段,某些工具,在程序运行时修改代码,实现运行时功能分支的控制
    2018-10-13
    1
    28
  • feng
    第一个实验做的不严谨,第一,木有定义HelloWord类,第二,没有执行编译操作,不知道是有意为之,还是不小心把步骤漏掉了
    2018-10-07
    1
    10
  • the geek
    大概说一下我自己的理解(望老师指正): 1. Agent就是一个调用JVMTI函数的一个程序。 2. JVMTI能够提供的函数能够获得JVM的运行信息,还可以修改JVM的运行态。 3. JVMTI能够修改JVM运行态是因为JVM已经在运行流程中埋下了钩子函数,JVMTI中的函数可以传递具体逻辑给钩子函数。 4. JVMTI函数是C语言实现的JNI方法。 5. 通过Instrumentation我们可以用Java语言调用大部分JVMTI函数。 6. JVM在启动时会加载Agent 入口函数Agent_OnLoad,我们可以在此函数中注册Agent。 7. JVM在运行中可以通过Agent_OnAttach函数来加载Agent,我们可以在此函数中注册Agent。 8. B虚拟机调用attach方法attach到A虚拟机后,可以将Agent程序作为参数调用A虚拟机的Agent_OnAttach函数。 9. premain方法中的程序逻辑会被注册到Agent_OnLoad函数中。 10. agentmain方法中的程序逻辑会被注册到Agent_OnAttach函数中。 11. 在premain或agentmain方法中的拿到的Instrumentation引用,可以理解成拿到了JVMTI的引用(大部分函数)。 以上全是个人抽象理解,不是具体实现。
    2020-02-12
    6
  • 随心而至
    把老师给的程序都跑了一篇,发现想要彻底搞懂,还需要多学习,C/C++的知识不能丢了,因为HotSpot JVM 的源码基本上都是用它来实现的。 不过跑了一下代码,最起码可以表面上搞懂了像Lombok,AOP这些都是如何实现的。
    2019-11-01
    3
  • 饭粒
    profiler 示例,文中省略了 HelloWorld.java 和编译提及下更好。 # cat HelloWorld.java public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); HelloWorld w = new HelloWorld(); } } # java -javaagent:myagent.jar -cp $CLASS_PATH:./asm-7.0-beta.jar:./asm-tree-7.0-beta.jar HelloWorld Hello World! HelloWorld: 1
    2019-12-28
    1
  • 一缕阳光
    实习的时候有幸做过一个利用Instrumentation实现自动打点和性能监控的项目。受益匪浅啊 哈哈哈哈 ,不得不说里面坑还是挺多的
    2019-07-07
    1
  • 奇奇
    ASM7 GETSTATIC这些常量是哪里来的?
    2019-04-29
    1
    1
收起评论
显示
设置
留言
12
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部