The Cycling Tour โ€“ Java's Fraught Relationship with Cyclic Object Graphs by Stuart Marks

Learn how Java handles cyclic object references, common pitfalls in serialization and collections, and best practices for avoiding stack overflows and memory issues in circular structures.

Key takeaways
  • Cycles in object graphs can cause serious issues in Java, particularly with serialization, collections, and static initialization

  • Common problems with cyclic object graphs include:

    • Stack overflow errors during serialization/deserialization
    • NullPointerExceptions due to partially initialized objects
    • HashCode/equals contract violations
    • Class initialization circularities
    • Memory leaks and corruption
  • The JVM provides protections against cyclic class dependencies but object cycles must be handled explicitly by developers

  • Best practices for handling cycles:

    • Avoid direct object references where possible
    • Use identity-based comparisons instead of value-based for cyclic structures
    • Declare fields transient to break serialization cycles
    • Minimize work done in static initializers
    • Use side tables/maps to track visited objects during traversal
  • Collections (especially Set/Map) are particularly vulnerable to cycles because:

    • Their hashCode/equals methods traverse all elements
    • They rely on object state that may be partially initialized
    • Cycles can cause infinite recursion during lookups
  • Serialization strategies for cyclic graphs:

    • Use custom serialization with readObject/writeObject
    • Store object references in arrays/identifiers instead of direct references
    • Maintain external object tables during serialization
    • Break cycles by making certain fields transient
  • Graph traversal algorithms need special handling for cycles:

    • Track visited nodes to detect cycles
    • Consider using non-recursive implementations
    • Define clear semantics for cycle handling
    • Be prepared to break cycles when needed
  • Add diagnostic tooling and logging to detect cycles early:

    • Class resolution logging
    • JFR events for initialization cycles
    • Graph visualization tools
    • Clear error messages identifying cycle paths