Got an issue: java.lang.ClassFormatError: Duplicate method name "listIterator"

53 views
Skip to first unread message

satoshi hikida

unread,
Mar 26, 2025, 1:28:29 AMMar 26
to Byte Buddy
Hi,

I'm creating a simple tool using Byte Buddy to trace method calls.
While testing it on an application that uses the Guava library's `ImmutableList`, I encountered the following `java.lang.ClassFormatError`.

``` shell
$ ./gradlew run
Reusing configuration cache.

> Task :app:run FAILED
premain!! arges:null
tracer start!!
org.example.App#main :: is called.
tracer end!!
Exception in thread "main" java.lang.ClassFormatError: Duplicate method name "listIterator" with signature "(I)Ljava.util.ListIterator;" in class file com/google/common/collect/ImmutableList$ReverseImmutableList
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1027)
        at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
        at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
        at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
        at org.example.App.main$original$LSGQf8fD(App.java:16)
        at org.example.App.main(App.java)

FAILURE: Build failed with an exception.
...
```

Is there a way to work around this issue?  

I would greatly appreciate any advice.  

For reference, my execution environment is as follows:
- OS: macOS Ventura (version 13.4)
- Java: OpenJDK version 21.0.6 2025-01-21 LTS

Here is the sample application code.
```
import com.google.common.collect.ImmutableList;

public class App {
  public static void main(String[] args) {
    ImmutableList<String> alist = ImmutableList.of("a", "b", "c");
    System.out.println(alist.get(0));
  }
}
```

And here is the `build.gradle` file.
```
plugins {
    id 'application'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation libs.guava
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    mainClass = 'org.example.App'
    applicationDefaultJvmArgs = ["-javaagent:../my-tracer-all.jar"]
}
```

Here is the code for the tracing tool.
```java
package org.example;

import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.matcher.ElementMatchers;

public class TraceAgent {
  public static void premain(String args, Instrumentation instrumentation) {
    System.out.println("premain!!");

    AgentBuilder agentBuilder =
        new AgentBuilder.Default()
            .type(ElementMatchers.any())
            .transform(
                (builder, typeDescription, classLoader, module, protectionDomain) ->
                    builder
                        .method(ElementMatchers.any())
                        .intercept(
                            MethodDelegation.to(MethodLogger.class)
                                .andThen(SuperMethodCall.INSTANCE)));
    agentBuilder.with(AgentBuilder.Listener.StreamWriting.toSystemError());
    agentBuilder.installOn(instrumentation);
  }

  public static class MethodLogger {
    @RuntimeType
    public static void onMethodEnter(@Origin Method method) {
      System.out.println("tracer start!!");
      try {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

        StackTraceElement caller = null;
        if (stackTrace.length > 3) {
          caller = stackTrace[3];
        }

        if (caller != null) {
          System.out.printf(
              "caller::%s#%s (%s:%d), callee::%s#%s\n",
              caller.getClassName(),
              caller.getMethodName(),
              caller.getFileName(),
              caller.getLineNumber(),
              method.getDeclaringClass().getCanonicalName(),
              method.getName());

        } else {
          System.out.printf(
              "%s#%s :: is called.\n",
              method.getDeclaringClass().getCanonicalName(), method.getName());
        }
      } catch (Exception e) {
        System.out.println("An exception occurred: " + e.getMessage());
      }
      System.out.println("tracer end!!");
    }
  }
}
```

Best regards,
Satoshi Hikida

Rafael Winterhalter

unread,
Mar 26, 2025, 6:41:13 AMMar 26
to satoshi hikida, Byte Buddy
That is strange. I can look into it if you created a reproducer. I assume that Google already manipulate the class file in a way. For mere decoration, Advice and .visit are normally a better option.
Best regards, Rafael

--
You received this message because you are subscribed to the Google Groups "Byte Buddy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to byte-buddy+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/byte-buddy/df00c50f-ecc8-4c31-9590-2a3e6e709f31n%40googlegroups.com.

satoshi hikida

unread,
Apr 3, 2025, 9:41:39 AMApr 3
to Byte Buddy
Hi, Rafael

Thank you for your reply.

I've created the reproducer as a GitHub repository. Please refer to it.
https://github.com/reddikih/bd-issue

Please let me know if there are any better ways to set up a reproducer.


> For mere decoration, Advice and .visit are normally a better option.
I see. It may be more suitable for my purpose. I'll try that one as well. Thank you.

Best regards,
Satoshi Hikida

Rafael Winterhalter

unread,
Apr 4, 2025, 6:38:48 AMApr 4
to satoshi hikida, Byte Buddy
This seems like a bug. I understand it such that Byte Buddy tries to generate bridge methods, which are missing, but also instruments the methods. I will need to see how I can avoid this.

Rafael Winterhalter

unread,
Apr 4, 2025, 9:33:14 AMApr 4
to satoshi hikida, Byte Buddy
The Guava class declares some bridges that are of the wrong return type what is strange. As a consequence, Byte Buddy does not discover the previous declaration and declares the bridges again. I will need to add some mechanism that suppresses this.

Rafael Winterhalter

unread,
Apr 5, 2025, 1:19:41 PMApr 5
to satoshi hikida, Byte Buddy
I think I found a fix. Could you run things with the latest master change set? You have to build Byte Buddy for this. I tried it and it seems to work. If you do not have any objections, this will be part of the next release.
Cheers, Rafael

satoshi hikida

unread,
Apr 6, 2025, 2:59:42 AMApr 6
to Byte Buddy
I've tried re-run the reproducer with the master branch of the ByteBuddy, and confirmed it completed successfully.
I also tried to run another application, for which I found this issue the first time, and it completed successfully, too.

Thank you for the quick fix!!

Regards,
Satoshi

Appendix. Here is the execution log.
```
$ java -javaagent:agent/build/libs/my-agent-all.jar -jar app/build/libs/app-all.jar
premain!!
tracer start
issue.example.Main#main :: is called.
tracer end
tracer start
com.google.common.collect.ObjectArrays#checkElementsNotNull :: is called.
tracer end
tracer start
com.google.common.collect.ObjectArrays#checkElementsNotNull :: is called.
tracer end
tracer start
com.google.common.collect.ObjectArrays#checkElementNotNull :: is called.
tracer end
tracer start
com.google.common.collect.ObjectArrays#checkElementNotNull :: is called.
tracer end
tracer start
com.google.common.collect.ObjectArrays#checkElementNotNull :: is called.
tracer end
tracer start
com.google.common.collect.RegularImmutableList#get :: is called.
tracer end
a
```

Reply all
Reply to author
Forward
0 new messages