Java Agent执行字节码替换时卡住无响应,寻求问题排查方案
问题分析与解决方案
卡住的根本原因
你遇到的程序卡死问题,核心在于在ClassFileTransformer的transform方法中调用ClassPool.get("Defender")会触发目标类的加载流程,而此时Defender类正处于加载过程中,会再次回调transform方法,形成无限循环,导致程序无法继续执行。
从你的输出可以看到,程序停在了"I'm here"之后,说明卡在了CtClass cc = cp.get("Defender");这一行——这行代码会触发类加载器去加载Defender类,而类加载的过程又会调用你的transform方法,陷入死循环。
修复后的Agent代码
修改核心逻辑:直接使用传入的classfileBuffer创建CtClass,避免触发额外的类加载;同时处理完后释放CtClass资源防止内存泄漏。
import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import java.io.ByteArrayInputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; public class AgentMain { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("Ha, ha! This program was hacked ;-)"); inst.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if ("Defender".equals(className)) { try { System.out.println("I'm here"); ClassPool cp = ClassPool.getDefault(); System.out.println("Class pool created"); // 直接使用传入的类字节码创建CtClass,避免触发类加载 CtClass cc = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); CtMethod method = cc.getDeclaredMethod("checkSign"); String newBody = "{" + " System.out.println(\"Verification successfully!\");" + " return;" + // 显式return,和原方法的void返回一致 "}"; method.setBody(newBody); byte[] modifiedBytecode = cc.toBytecode(); // 释放CtClass资源,避免内存泄漏 cc.detach(); return modifiedBytecode; } catch (Exception e) { e.printStackTrace(); } } return classfileBuffer; } }); } }
额外注意点
- 显式添加return语句:原方法是
void类型,虽然Javassist会自动补全,但显式添加return能让逻辑更清晰,避免潜在的字节码验证问题。 - 释放CtClass资源:调用
cc.detach()可以将CtClass从ClassPool中移除,防止内存泄漏,这在Agent这类长期运行的代码中很重要。 - 异常处理:如果后续仍有异常,可以考虑将异常信息输出到更明确的位置(比如文件),避免因为类加载过程中的异常无法在控制台显示。
内容的提问来源于stack exchange,提问作者Артем Лебідь
相关产品推荐
相关产品推荐

