attach java_基于Java Agent的attach方式实现方法耗时监控

在上一篇中我们已经介绍了java agent的相关概念和思想,给出了premain方式的实现代码。本篇主要是实现了attach方式,不同之处主要如下:premain是静态修改,在类加载之前修改; attach是动态修改,在类加载后修改要使premain生效重启应用,而attach不重启应用即可修改字节码并让其重新加载可以看到attach的方式更加强大,其核心原理首先是找到相关的进程id, 然后根据...
摘要由CSDN通过智能技术生成

在上一篇中我们已经介绍了java agent的相关概念和思想,给出了premain方式的实现代码。本篇主要是实现了attach方式,不同之处主要如下:

premain是静态修改,在类加载之前修改; attach是动态修改,在类加载后修改

要使premain生效重启应用,而attach不重启应用即可修改字节码并让其重新加载

可以看到attach的方式更加强大,其核心原理首先是找到相关的进程id, 然后根据进程id去动态修改相关字节码,具体的修改方式和premain无差,下面就直接给出详细实现。

项目结构(此处为了方便把主程序和Agent程序放在一起, 实际生产中肯定是分开的):

agentdemo

├── pom.xml

└── src

└── main

├── java

│ └── com

│ └── hebh

│ └── demo

│ ├── agent

│ │ ├── MyInstrumentationAgent.java

│ │ └── MyTransformer.java

│ └── application

│ ├── AgentLoader.java

│ ├── Launcher.java

│ ├── MyApplication.java

│ └── Runner.java

└── resources

├── META-INF

│ └── MANIFEST.MF

└── log4j2.xml

先看测试结果:

打成jar包

mvn clean package

运行主程序

java -jar target/myAgent-jar-with-dependencies.jar

运行agent程序, 注意带上系统的lib目录

java -Djava.ext.dirs=${JAVA_HOME}/lib -jar target/myAgent-jar-with-dependencies.jar LoadAgent

如下图所示,可以看到首先找到主程序的进程id为4477,然后再attach上去

1e2d970e3661

image-20190126202600306

然后此时再看主程序的运行日志, 可以看到在attach后动态增加的字节码生效了,实现了方法耗时监控:

1e2d970e3661

image-20190126203002824

详细代码:

MANIFEST.MF

Main-Class: com.hebh.demo.application.Launcher

Agent-Class: com.hebh.demo.agent.MyInstrumentationAgent

Can-Redefine-Classes: true

Can-Retransform-Classes: true

pom

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/m

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
宿主应用不能直接调用Java Agent中的静态方法,因为Java Agent是在独立的Java虚拟机中运行的,与宿主应用是独立的进程。Java Agent和宿主应用之间是通过Java Instrumentation API进行通信的。 在Java Agent中,可以通过premain方法获取到Instrumentation实例,并保存在静态变量中。然后,在宿主应用中,可以通过调用Java Agent提供的公共接口来获取Java Agent中的静态变量或执行Java Agent中的方法。 以下是一个示例: Java Agent中的公共接口: ``` public class MyAgent { private static Instrumentation instrumentation; public static void premain(String args, Instrumentation inst) { instrumentation = inst; } public static Object getObjectThreadLocal(Thread thread, ThreadLocal threadLocal) { return instrumentation.getObjectThreadLocal(thread, threadLocal); } // 其他公共方法 } ``` 宿主应用中的代码: ``` import java.lang.instrument.Instrumentation; public class MyApp { public static void main(String[] args) { // 加载Java Agent String agentJarPath = "/path/to/agent.jar"; String agentArgs = "arg1,arg2"; Instrumentation instrumentation = loadAgent(agentJarPath, agentArgs); // 调用Java Agent中的公共接口 Thread targetThread = // 获取目标线程 ThreadLocal threadLocal = // 获取目标线程中的ThreadLocal对象 Object threadLocalValue = MyAgent.getObjectThreadLocal(targetThread, threadLocal); // 其他业务逻辑 } private static Instrumentation loadAgent(String agentJarPath, String agentArgs) { try { // 通过VirtualMachine.loadAgent方法加载Java Agent VirtualMachine vm = VirtualMachine.attach("pid"); vm.loadAgent(agentJarPath, agentArgs); return MyAgent.getInstrumentation(); } catch (Exception e) { e.printStackTrace(); return null; } } } ``` 在这个示例中,宿主应用通过VirtualMachine.loadAgent方法加载Java Agent,并调用Java Agent提供的公共接口MyAgent.getObjectThreadLocal来获取ThreadLocal变量。需要注意的是,加载Java Agent需要使用VirtualMachine.attach方法,该方法需要传递Java虚拟机的进程ID(pid)。可以通过jps命令来获取Java虚拟机的进程ID。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值