jvm “链接错误:使用ASM动态检测Java类时尝试重复类定义”

5hcedyr0  于 2022-11-07  发布在  Java
关注(0)|答案(1)|浏览(147)

我自己写了一个javaagent来用ASM动态地插装Java类(我没有使用ASM的COMPUTE_MAXSCOMPUTE_FRAMES,我自己手动地做)。实际上,我只是试图使用一个大的try-catch块来捕获未捕获的异常或错误,并记录这些事件(我的代码实际上是this question中代码的修订版本)。
但是,当我尝试在开源项目joda-time的测试过程中使用我的javaagent时,出现了以下错误:

org.apache.maven.surefire.testset.TestSetFailedException: org.joda.time.TestAllPackages
    at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:116)
    at org.apache.maven.surefire.junit.JUnit3Provider.executeTestSet(JUnit3Provider.java:140)
    at org.apache.maven.surefire.junit.JUnit3Provider.invoke(JUnit3Provider.java:113)
    at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:379)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:340)
    at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:413)
Caused by: java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "org/joda/time/DateTimeZone"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    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:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.joda.time.TestChronology.<clinit>(TestChronology.java:47)
    at org.joda.time.TestAll.suite(TestAll.java:37)
    at org.joda.time.TestAllPackages.suite(TestAllPackages.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.maven.surefire.common.junit3.JUnit3Reflector.createInstanceFromSuiteMethod(JUnit3Reflector.java:157)
    at org.apache.maven.surefire.common.junit3.JUnit3Reflector.constructTestObject(JUnit3Reflector.java:124)
    at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:75)
    ... 6 more

据我所知,每次sun/misc/Launcher$AppClassLoader试图加载一个类时,都会进入ClassFileTransformertransform方法。在我修改类并返回修改后的字节数组后,类最终被加载。因此,每个类应该只加载一次。
我进一步尝试打印出类名和在transform方法开始时加载它的加载器,我发现org/joda/time/DateTimeZone只出现一次,这与我的理解一致。
所以现在唯一与我的理解不一致的是错误。为什么sun/misc/Launcher$AppClassLoader attempted duplicate class definition与我的代理?每一个都去了这么soomly当我删除我的-javaagent选项。

2izufjch

2izufjch1#

要计算帧,ASM需要在跳转指令的目标处找到几个类的公共超类。为此,ASM将加载这些类以浏览其层次结构。如果以这种方式加载类,同时在第一次加载时还对该类进行了检测,则该类将在检测后加载,最终将出现此错误。
为了避免这种情况,你可以覆盖ASM的ClassWriter的getCommonSuperClass方法。你需要解析传递给该方法的这些类的类文件,而不是加载它们。如果你想要这种开箱即用的实现,你可以使用Byte Buddy,它公开ASM并以这种方式解析它的ClassWriter。

相关问题