入门
agent
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
/**
* 描述
*/
public class PerfMonitorAgent {
/**
* 执行方法拦截
*
* @param agentArgs:-javaagent 命令携带的参数
* @param instrumentation:提供操作类定义的相关方法
*/
public static void premain(String agentArgs, Instrumentation instrumentation) {
System.out.println(PerfMonitorAgent.class.getSimpleName() + ".premain was called.");
ClassFileTransformer trans = new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("loader:" + loader.toString());
System.out.println("className:" + className);
System.out.println("classBeingRedefined:" + classBeingRedefined.getClass().getName());
return new byte[0];
}
};
System.out.println("Adding a ClassFileTransformer instance to the JVM.");
instrumentation.addTransformer(trans);
}
}
测试类
运行时需要通过 -javaagent:/myAgent.jar 将打包后的agent包注入到项目里面
import java.util.concurrent.TimeUnit;
public class Application {
public static void main(String[] args) throws InterruptedException {
System.out.println("main 函数 运行了 ");
BirdService service = new BirdService();
service.jiji();
service.gugu();
}
}
class BirdService {
public void jiji() throws InterruptedException {
System.out.println("ji ji ------------");
TimeUnit.SECONDS.sleep(5);
}
public void gugu() throws InterruptedException {
System.out.println("gu gu ------------");
TimeUnit.SECONDS.sleep(5);
}
}
maven配置
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>org.example.agent.PerfMonitorAgent</Premain-Class>
<Agent-Class>org.example.agent.PerfMonitorAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
<finalName>myAgent</finalName>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
运行效果
PerfMonitorAgent.premain was called.
Adding a ClassFileTransformer instance to the JVM.
loader:sun.misc.Launcher$AppClassLoader@18b4aac2
className:com/intellij/rt/execution/application/AppMainV2$Agent
loader:sun.misc.Launcher$AppClassLoader@18b4aac2
className:com/intellij/rt/execution/application/AppMainV2
loader:sun.misc.Launcher$AppClassLoader@18b4aac2
className:com/intellij/rt/execution/application/AppMainV2$1
loader:sun.misc.Launcher$AppClassLoader@18b4aac2
className:com/example/one/Application
main 函数 运行了
loader:sun.misc.Launcher$AppClassLoader@18b4aac2
className:com/example/one/BirdService
ji ji ------------
gu gu ------------
深入
Instrumentation
自JDK 1.5 版本开始新增了Instrumentation。 java.lang.instrument.Instrumentation接口类提供了检测Java编程语言代码所需的服务。插桩是将字节码添加到方法中,目的是收集供工具使用的数据。由于这些更改纯粹是附加的,所以这些工具不会修改应用程序的状态或行为。此类良性工具的示例包括监视代理、分析程序、覆盖率分析程序和事件记录程序。
有两种方法可以获得Instrumentation接口的实例:
- 当 JVM 以指示代理类的方式启动时。在这种情况下,Instrumentation实例被传递给代理类的
premain方法。 - JVM提供了在JVM启动后启动代理的机制。在这种情况下,Instrumentation实例被传递给代理代码的
agentmain方法。
一旦代理获得了Instrumentation实例,代理就可以随时调用实例上的方法。
在 JDK 中 Instrumentation只有一个实现 sun.instrument.InstrumentationImpl
premain
基本概念
- 加载时机:JVM虚机运行时加载
MANIFEST.MF文件中需要有Premain-Class:信息
典型应用
agentmain
基本概念
- 加载时机:JVM虚机已启动后,使用
attach(String pid)的方式,对应JDK方法VirtualMachine.attach(String id)VirtualMachine.attach(VirtualMachineDescriptor vmd)
典型应用
阿里的监控诊断产品 Arthas 同时实现了 premain 和 agentmain。对于监控诊断工具而言,agentmain 已经够用。