调用Instrumentation#retransformClasses时Java Agent抛出UnsupportedClassVersionError
问题描述
我正在开发一个Java Agent,用于dump方法的调用者:通过ObjectWeb ASM修改目标方法,插入调用记录调用者与方法内部状态的dump函数。相关环境与问题如下:
- Agent基于Java 11.0.20编译(Gradle构建),通过
-javaagent命令参数启动 - ASM版本为9.6(已确认支持JDK 8与JDK 17)
- 目标Jar是基于Java 8编译的Bukkit-1.8.8,运行在Java 17环境中
- 调用
Instrumentation#retransformClasses时抛出UnsupportedClassVersionError,导致程序崩溃 - 预期结果:Agent将dump内容写入文件;实际结果:程序崩溃
完整报错日志:
Exception in thread "main" java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:491) at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:503) Caused by: java.lang.UnsupportedClassVersionError at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method) at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:169) at me.cire3.sfbf.SFBFAgentEntryPoint.transform(SFBFAgentEntryPoint.java:39) at me.cire3.sfbf.SFBFAgentEntryPoint.premain(SFBFAgentEntryPoint.java:17) ... 6 more *** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message Outstanding error when calling method in invokeJavaAgentMainMethod at s\src\java.instrument\share\native\libinstrument\JPLISAgent.c line: 619 *** java.lang.instrument ASSERTION FAILED ***: "success" with message invokeJavaAgentMainMethod failed at s\src\java.instrument\share\native\libinstrument\JPLISAgent.c line: 459 *** java.lang.instrument ASSERTION FAILED ***: "result" with message agent load/premain call failed at s\src\java.instrument\share\native\libinstrument\JPLISAgent.c line: 422 FATAL ERROR in native method: processing of -javaagent failed, processJavaStart failed
问题分析
核心原因是类文件版本不匹配:
- ASM默认会以当前运行JVM的版本(Java 17,版本号61)生成修改后的类文件,但目标类原始版本为Java 8(版本号52),JVM在retransform时检测到版本不一致,抛出
UnsupportedClassVersionError。 - Agent本身以Java 11编译(版本号55),虽然JVM能加载Agent类,但在retransform操作的版本校验环节触发异常。
解决方案
1. 调整Gradle编译配置,强制Agent兼容Java 8
在build.gradle中设置源兼容性与目标兼容性为Java 8,确保Agent类文件版本为52:
java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 }
若使用模块化项目,需同步调整module-info.java的兼容性声明。
2. ASM修改类时显式复用原始类版本
在ClassFileTransformer的transform方法中,解析原始类的版本号,并传递给ClassWriter,确保修改后的类版本与原始类一致:
@Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // 仅处理目标类 if (!className.equals("your/target/class/FullName")) { return null; } ClassReader cr = new ClassReader(classfileBuffer); // 获取原始类的版本号(前4字节) int originalClassVersion = cr.readInt(); // 使用原始版本创建ClassWriter,保留原有类结构 ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); // 自定义ClassVisitor时,确保生成的指令符合Java 8规范 ClassVisitor cv = new YourCustomClassVisitor(cw, originalClassVersion); cr.accept(cv, 0); return cw.toByteArray(); }
3. 确保retransform配置正确
启动Agent时,需将addTransformer的第二个参数设为true,明确支持retransform操作:
public static void premain(String agentArgs, Instrumentation inst) { inst.addTransformer(new YourClassTransformer(), true); // 触发retransform try { inst.retransformClasses(TargetClass.class); } catch (Exception e) { e.printStackTrace(); } }
4. 验证类版本
修改完成后,可通过javap -verbose path/to/modified/class.class查看类文件版本,确认版本号为52(对应Java 8)。
内容的提问来源于stack exchange,提问作者a a
相关产品推荐
相关产品推荐

