使用Bytekit 做Agent 对所有类做增强时,会出现`duplicate class definition`的错误
- Dominant language
- Java
- Stars
- 525
- Forks
- 115
- PR merge metrics
- No merged PRs in 30d
Description
环境: Windows 10 64 bit/Oracle JDK8
问题复现的demo(必现问题) -> https://github.com/kongwu-/bytekit-agent-sample
Transform类很简单,什么增强内容都没做,只是调用了一下AsmUtils.toClassNode
```java
public class SmpClassTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("enhance... "+className);
try {
ClassNode classNode = AsmUtils.toClassNode(classfileBuffer);
// 获取增强后的字节码
// 打印ClassLoader 日志之后,发现这里会进行Class load,而且ClassLoader是Launcher$AppClassLoader
// 这个toBytes 会 load class 有点不太理解,只是生成字节码需要 define 一次嘛……
byte[] bytes = AsmUtils.toBytes(classNode);
//transformer 返回后,会再次打印一遍Class Load日志,ClassLoader 仍然是 Launcher$AppClassLoader
return bytes;
}catch (Error e){
e.printStackTrace();
return classfileBuffer;
}
}
```
最后打包 Agent 运行时,就会出现重复定义类的错误:`loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "org/springframework/core/NestedRuntimeException"`(理论上很多类都会出现这个问题,只是碰巧报错的是Spring的)。
transform 方法中load 了两遍,也没报错,这里应该算是第三遍load了,此时发生错误:duplicate class definition
stack trace 也有一点奇怪:
```
Exception in thread "main" java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "org/springframework/core/NestedRuntimeException"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
//native define Class 之后,又调用 loadClass
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at org.example.App.main(App.java:13)
```
按道理说,同一个ClassLoader,define一次之后就被缓存了,再次loadClass时就会从缓存中获取了,这里报错实在想不通。
Contributor guide
No contributing guide indexed for this repository
Research direction
Reproduce the issue with the bytekit-agent-sample on Windows 10 and Oracle JDK8, starting from SmpClassTransformer.transform and the AsmUtils.toClassNode/toBytes calls. Trace the class-loading sequence through org.example.App.main at App.java:13 and the reported AppClassLoader stack. Done means identifying why duplicate definition occurs and documenting or validating a fix for the repeated loading path.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100