使用 ASM 动态检测 Java 类时出现“LinkageError:尝试重复的类定义”

问题描述 投票:0回答:2

我自己写了一个javaagent,用ASM动态检测java类(我没有使用ASM的

COMPUTE_MAXS
COMPUTE_FRAMES
,我自己手动完成)。实际上,我只是尝试对非构造函数方法使用一个大的 try-catch 块来捕获未捕获的异常或错误并记录此类事件(我的代码实际上是这个问题中代码的修订版本)。

但是,当我在一个开源项目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
尝试加载类时,都会输入
transform
ClassFileTransformer
方法。在我修改了类并返回修改后的字节数组后,该类终于被加载了。因此,每个类应该只加载一次。

我进一步尝试在

transform
方法的开头打印出类名和加载它的加载器,我发现
org/joda/time/DateTimeZone
只出现一次,这与我的理解一致。

所以现在唯一与我的理解不一致的是错误。为什么与我的经纪人

sun/misc/Launcher$AppClassLoader attempted duplicate class definition
?当我删除我的
-javaagent
选项时,一切都进展顺利。

java jvm java-bytecode-asm javaagents bytecode-manipulation
2个回答
2
投票

为了计算帧,ASM 需要在跳转指令的目标处找到多个类的公共超类。为此,ASM 加载类以浏览它们的层次结构。如果您以这种方式加载一个类,而该类在第一次加载期间也已被检测,则该类将在检测后加载,并且最终会出现此错误。

为了避免这种情况,可以重写ASM的ClassWriter的getCommonSuperClass方法。您需要解析传递给该方法的这些类的类文件,而不是加载它们。如果您想要开箱即用的实现,您可以使用 Byte Buddy,它公开 ASM 并以这种方式解析其 ClassWriter。


0
投票

我在尝试从 IntelliJ IDEA 运行配置时遇到了这个问题 - 使用 Maven 重新加载项目有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.