java module_error ai_generated true

java.lang.ClassCircularityError: <class name>

ID: java/class-circularity-error

Also available as: JSON · Markdown · 中文
80%Fix Rate
82%Confidence
1Evidence
2024-01-18First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
Java 8 active
Java 11 active
Java 17 active
Java 21 active

Root Cause

The JVM detected a circular dependency during class initialization, where class A's static initializer triggers loading of class B, which in turn triggers loading of class A again before A's initialization is complete, causing an infinite loop that the JVM cannot resolve.

generic

中文

JVM在类初始化期间检测到循环依赖,其中类A的静态初始化器触发了类B的加载,而类B又触发了类A的加载,此时A的初始化尚未完成,导致JVM无法解决的无限循环。

Official Documentation

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ClassCircularityError.html

Workarounds

  1. 85% success Refactor the circular dependency by moving shared static fields or methods to a third utility class. For example, if class A uses `B.CONSTANT` and class B uses `A.CONSTANT`, create a `Constants` class: `public class Constants { public static final int VALUE = 42; }` and have both A and B reference it.
    Refactor the circular dependency by moving shared static fields or methods to a third utility class. For example, if class A uses `B.CONSTANT` and class B uses `A.CONSTANT`, create a `Constants` class: `public class Constants { public static final int VALUE = 42; }` and have both A and B reference it.
  2. 80% success Use lazy initialization for static fields that cause the cycle. Replace `public static final Foo foo = new Foo();` with a holder class pattern: `private static class Holder { static final Foo foo = new Foo(); }` and access it via `Holder.foo`. This defers loading until the field is actually accessed.
    Use lazy initialization for static fields that cause the cycle. Replace `public static final Foo foo = new Foo();` with a holder class pattern: `private static class Holder { static final Foo foo = new Foo(); }` and access it via `Holder.foo`. This defers loading until the field is actually accessed.
  3. 75% success If using a dependency injection framework like Spring, ensure beans are not involved in circular references. Use `@Lazy` annotation on one of the beans: `@Lazy @Component public class A { ... }` to break the initialization cycle.
    If using a dependency injection framework like Spring, ensure beans are not involved in circular references. Use `@Lazy` annotation on one of the beans: `@Lazy @Component public class A { ... }` to break the initialization cycle.

中文步骤

  1. Refactor the circular dependency by moving shared static fields or methods to a third utility class. For example, if class A uses `B.CONSTANT` and class B uses `A.CONSTANT`, create a `Constants` class: `public class Constants { public static final int VALUE = 42; }` and have both A and B reference it.
  2. Use lazy initialization for static fields that cause the cycle. Replace `public static final Foo foo = new Foo();` with a holder class pattern: `private static class Holder { static final Foo foo = new Foo(); }` and access it via `Holder.foo`. This defers loading until the field is actually accessed.
  3. If using a dependency injection framework like Spring, ensure beans are not involved in circular references. Use `@Lazy` annotation on one of the beans: `@Lazy @Component public class A { ... }` to break the initialization cycle.

Dead Ends

Common approaches that don't work:

  1. Remove static initializers entirely from the affected classes 75% fail

    Static initializers often hold essential configuration or resource loading. Removing them may break the application's functionality. The circular dependency should be resolved by refactoring, not by elimination.

  2. Add Thread.sleep() in static initializers to break the cycle 95% fail

    Sleeping does not break the circular dependency; it only delays the crash. The JVM will still detect the cycle when the other class is loaded, and the error will occur after the sleep.