Skip to content

[BUG][1.21.1]Memory leak in VoxelShapeCache.mergeShapeCache (unbounded cache leads to OOM) #473

@1341254443

Description

@1341254443

Description:

We are experiencing a severe memory leak on a modded Minecraft server (1.21.1) caused by the mergeShapeCache inside codechicken.lib.raytracer.VoxelShapeCache. The cache uses expireAfterAccess(2, HOURS) but no maximum size or weak reference, causing it to accumulate millions of ImmutableSet<VoxelShape> entries over time. Each entry holds VoxelShape objects which internally contain large double[] arrays (or DoubleArrayList), eventually consuming 90%+ of the heap (14+ GB) and causing server freezes / crashes.

Environment:

  • Minecraft version: 1.21.1
  • Mod loader: neoforge 21.1.221
  • CCL version: 4.6.1.526

Evidence:

1. Heap dump analysis (VisualVM)

  • double[] instances: 73.7 million (3.5 GB)
  • DoubleArrayList instances: 73.7 million (1.6 GB)
  • ArrayVoxelShape instances: 22 million (842 MB)
  • Heap usage: 14.2 GB / 15.6 GB (90.67%)

2. GC Root path (from VisualVM)

static mergeShapeCache in class codechicken.lib.raytracer.VoxelShapeCache
→ localCache (Guava LocalCache)
→ segments[]
→ table (AtomicReferenceArray)
→ StrongAccessEntry
→ key = ImmutableSet
→ value = merged VoxelShape
→ xs = double[] / DoubleArrayList

3. Code snippet (from current CCL version)

private static final Cache<ImmutableSet<VoxelShape>, VoxelShape> mergeShapeCache =
   CacheBuilder.newBuilder()
       .expireAfterAccess(2L, TimeUnit.HOURS)
       .build();

No maximumSize or weakValues is set.

Root cause:

mergeShapeCache never evicts entries as long as they are accessed every 2 hours. In a busy server, many distinct shape sets are constantly created, and the cache grows indefinitely.

Keys (ImmutableSet) strongly reference VoxelShape objects, which in turn hold large primitive arrays. This prevents GC from recovering memory.

The leak is amplified by mods that frequently call VoxelShapeCache.merge() (e.g., raytracing for entity culling, block rendering, or Translocators' pipe rendering).

Suggested fix:

Add a reasonable maximum size and/or use weak references:

private static final Cache<ImmutableSet<VoxelShape>, VoxelShape> mergeShapeCache =
    CacheBuilder.newBuilder()
        .maximumSize(5000)                         // limit number of entries
        .expireAfterAccess(1, TimeUnit.HOURS)      // shorter expiry
        .weakValues()                              // allow GC to collect values
        .build();

Alternatively, at least set .maximumSize(10000) to prevent unbounded growth.
Impact:

Without this fix, servers running any mod that uses CCL's VoxelShapeCache will eventually run out of memory, requiring frequent restarts.

Thank you for your great work!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions