
概念Java Agent 是一种特殊的 Java 程序通过 Instrumentation API 在 JVM 启动时premain或运行时agentmain动态修改或增强字节码。常用于性能监控、代码热修复、AOP 等场景。premain是在jvm启动的时候类加载到虚拟机之前执行的需在 MANIFEST.MF 中声明Premain-Class。agentmain是可以在jvm启动后类已经加载到jvm中了才去转换类。 这种方式会转换会有一些限制比如不能增加或移除字段。需在 MANIFEST.MF 中声明Agent-Class并通过VirtualMachineAPI 附加到目标 JVM。具体的做法,两者的实际做法是差不多的premain定义个静态方法public static void premain(String args, Instrumentation inst),在java 的启动参数中添加 -javaagent:/jar包路径[agentArgs] 这样定义了后jvm启动时就会去加载javaagent中指定的jar包查找MANIFEST.MF文件中Premain-Class属性的类执行premain方法。Manifest-Version: 1.0 Can-Redefine-Classes: true Premain-Class: com.premian.MyAgent Can-Retransform-Classes: trueimport java.lang.instrument.Instrumentation; import java.lang.management.ManagementFactory; Slf4j public class PreMain { public static void premain(String agentArgs, Instrumentation inst) { log.debug(-- during jvm pre main run... --); // agentLogic(agentArgs, inst); String jvmPid jvmPid(); try { FileUtil.writeToFile(System.getProperty(user.dir) /pid, jvmPid); } catch (IOException e) { e.printStackTrace(); log.error([ERROR] write pid to file error.); } log.debug(-- premain get class end --\n); } private static String jvmPid() { String thisJvmName ManagementFactory.getRuntimeMXBean().getName(); String thisJvmPid thisJvmName.split()[0]; log.debug(-- this jvm process pid: thisJvmPid \n); return thisJvmPid; } }agentmain定义个静态方法public static void agentmain(String agentOps, Instrumentation instrumentation),在生成jar包中MANIFEST.MF文件中需要有Agent-Class: xxx.xxx (xxx.xxx就是上面agentmain方法所在的类名)Can-Retransform-Classes: trueManifest-Version: 1.0 Can-Redefine-Classes: true Agent-Class: cn.think.in.java.clazz.loader.asm.agent.AgentMainTraceAgent Can-Retransform-Classes: true使用下面代码将agent添加到指定java进程 public class AgentMain { public static void agentmain(String agentArgs, Instrumentation inst) { vm VirtualMachine.attach(pid); try { vm.loadAgent(D:\\tmp\\my-java-agent-1.0-jar-with-dependencies.jar, null); } finally { vm.detach(); } }注意事项JVM 参数启动时加载-javaagent:/path/to/agent.jarargs运行时加载依赖tools.jarJDK 路径下。安全限制某些 JVM 实现可能限制运行时附加。需开启Can-Redefine-Classes和Can-Retransform-Classes。性能影响频繁的字节码操作可能导致性能开销。常见工具与框架Byte Buddy简化字节码操作。Javassist动态生成和修改类。Arthas基于 Agent 的运行时诊断工具。