java.lang.ClassCircularityError: <class name>
ID: java/class-circularity-error
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 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.htmlWorkarounds
-
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. -
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. -
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.
中文步骤
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.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.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:
-
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.
-
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.