Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-245406

Memory leak leading to crashes when loading pre-1.18 chunks

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • 22w03a
    • 1.18.1
    • openjdk version "17.0.1" 2021-10-19
      OpenJDK Runtime Environment Temurin-17.0.1+12 (build 17.0.1+12)
      OpenJDK 64-Bit Server VM Temurin-17.0.1+12 (build 17.0.1+12, mixed mode, sharing)

      Linux 5.15.2-arch1-1
    • Plausible
    • Crash
    • Very Important

      For chunks generated in Minecraft 1.17.1 (and presumably older versions) and subsequently loaded using Minecraft 1.18.1, memory usage rapidly increases until the server crashes. This only happens the first time these chunks are loaded.

      This is reproducible in vanilla with no mods by loading a pre-1.18 world with a large amount of generated chunks, and then causing a large amount of these old chunks to load. Although this is reproducible and has been tested to happen in a vanilla environment, a "world pregenerator" type mod which loads lots of chunks quickly is the quickest way to reproduce this issue.

      This was reported to Paper as 7094, and I have debugged the source to be ChunkAccesses holding onto NoiseChunks, which hold a Blender, which references a WorldGenRegion, which of course holds a list of ChunkAccess.

      The following diff solved the issue:

      diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
      index 96cb3e8cad9e7a5edd2a448ea88f2447104fbb5a..86cbb683f3e7292d29fc6bea1fb9ffaa018b3254 100644
      --- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
      +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
      @@ -410,11 +410,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
           }
       
           public NoiseChunk getOrCreateNoiseChunk(NoiseSampler noiseColumnSampler, Supplier<NoiseChunk.NoiseFiller> columnSampler, NoiseGeneratorSettings chunkGeneratorSettings, Aquifer.FluidPicker fluidLevelSampler, Blender blender) {
      -        if (this.noiseChunk == null) {
      -            this.noiseChunk = NoiseChunk.forChunk(this, noiseColumnSampler, columnSampler, chunkGeneratorSettings, fluidLevelSampler, blender);
      -        }
      -
      -        return this.noiseChunk;
      +        return NoiseChunk.forChunk(this, noiseColumnSampler, columnSampler, chunkGeneratorSettings, fluidLevelSampler, blender); // Paper - create a new one each time to avoid leaking
           }
      

      However I don't know enough about how and when the NoiseChunk is used to know if this is a correct solution, and as noted in this PR, it's probably best to clear the reference once the NoiseChunk is "done" being used rather than creating a new one each time getOrCreate is called, however I am not sure where/when the NoiseChunk is "no longer needed".

            panda4994 [Mojang] Panda
            jmp Jason
            Votes:
            9 Vote for this issue
            Watchers:
            9 Start watching this issue

              Created:
              Updated:
              Resolved:
              CHK: