Memory Leaks

Mythic Dungeons loads and unloads worlds on a regular basis in order to create private experiences for every party. This is a natively supported function of Bukkit (and consequently Spigot and its forks, such as Paper and Purpur.)

However, this is a system that is quite infrequently used. As such, there are a lot of plugins out there that don't properly work with it safely. This can cause major memory leaks, where Mythic Dungeons attempts to unload the world, but another plugin on the server keeps that world in memory.

Troubleshooting Memory Leaks

Most memory leaks in recent versions of Mythic Dungeons are caused by other plugins. We have done everything we can to mitigate them, but some plugins still keep the dungeon worlds alive. It can be difficult to diagnose, but here's some guidance on how to investigate the cause.

Read the console first

Before you take a heap dump, check the console. Mythic Dungeons reports a failed unload itself, and the message tells you which situation you are in:

ERROR :: Failed to unload dungeon world <name> - it is STILL LOADED. Leaving its files alone; this world and its name stay in use until the next restart.
-- Nobody is in the world, so another plugin cancelled WorldUnloadEvent.

That second line is the one you want. It means the world was empty and some other plugin cancelled WorldUnloadEvent, which is a plugin conflict, not a Mythic Dungeons bug. If instead the console lists players:

-- Still inside the world: <playername>

then somebody (or something holding a player reference) is still in the world, and the unload was correctly refused.

There is a third variant, printed when the unload throws:

ERROR :: Error encountered while unloading dungeon world <name>
-- The dungeon world is STILL LOADED! Please share this error with the developers!

That one comes with a stack trace. Share it.

Note that when an unload fails, Mythic Dungeons deliberately leaves the world's files on disk and keeps the instance name reserved until the next restart, rather than deleting files out from under a world the server still has open.

A note on timing

An empty instance is not disposed instantly, and that is by design:

  • The General.CleanupDelay option in the dungeon's config delays disposal by that many ticks after the last player leaves. A non-zero value there means an idle world is expected to hang around for a while.
  • A safety-net sweep runs every 5 seconds and disposes any instance that is loaded, has had at least one player, and is now empty. Instances that have never had a player are skipped, so a warm preloaded instance is not churned.
  • Disposal also refuses to run at all while any player is still in the instance.

So a dungeon world that lingers for a few seconds is normal. One that never goes away is worth investigating.

Another plugin can block disposal outright

DungeonDisposeEvent is cancellable. If any plugin cancels it, Mythic Dungeons puts the instance back and stops: the world is never unloaded and the files are never cleaned up. If you are chasing a world that refuses to disappear and the console is silent, check whether something on the server listens to that event. See Events.

"Memory leak protection is limited"

If you see this on startup:

WARNING: Memory leak protection is limited on this Minecraft version!
-- You should be fine, but if there are major memory leaks, submit an issue on the issue tracker and include the Minecraft version of your server!

it means Mythic Dungeons could not locate the internal fields it uses to clear a player's combat tracker on this server version. That tracker records the damage sources that hurt the player, which hold entity references, which in turn hold a reference to their world (see the entity section further down). The message is exactly what it says: usually harmless, but worth mentioning on the issue tracker along with your Minecraft version if you are also seeing dungeon worlds stay in memory.

Taking a heap dump

You will need to use Paper or a fork of Paper!

If your server has been online for a while and has accumulated a lot of RAM usage, run the command /paper heap and it will create what we call a heap dump in your server's 'dumps' folder. Heap dumps contain tons of valuable information for developers about how different plugins and systems are communicating with each other. They are often several GB large though, so make sure you've got plenty of disk space!

From here you have two options:

  1. Inspect the heap dump yourself using Eclipse Memory Analyzer
  2. Upload the heap dump to a file hosting site and share it on the issue tracker.

Inspecting Heap Dumps

Inspecting heap dumps is a VERY valuable skill to have, and is the recommended way to troubleshoot memory leaks caused by dungeon worlds! You'll need Eclipse Memory Analyzer (linked above). It's very easy to install and there are detailed instructions below on how to search for memory leaks.

NOTE: You may need a large amount of memory on your computer to inspect particularly big heap dumps!

First, you need to load up your heap dump. Open Eclipse Memory Analyzer, then from the top drop-down choose file and then open heap dump. Navigate to where you've saved it on your computer and open it.

It may take some time for your heap dump to finish loading, but once it does, just close the window the pops up. From here, you'll want to click the Open Dominator tree button indicated here.

Once that's open, you'll see a massive list like a spreadsheet. At the top will be a section labeled <Regex>. Click on it and type net.minecraft.server.level.ServerLevel, then press enter. You should see a list of worlds now.

If that returns nothing, search for net.minecraft.server.level.WorldServer instead. Which of the two names your dump contains depends on how your server is mapped, not on a single version cutoff: ServerLevel is the Mojang name and WorldServer is the older Spigot-mapped one. Mythic Dungeons' own reflection handles this the same way, trying the Mojang name first and falling back to the Spigot name; the source notes that from MC 26.1 the server jar is fully unobfuscated and Paper dropped its plugin remapper, so from that version the Spigot-mapped names no longer resolve at all. If you are unsure, search net.minecraft.server.level. and look at what comes back.

Right-click one of them and choose Path to GC Roots > exclude weak references. This will show you what is holding on to the world. The main thing you're looking for in the various drop-downs is other plugins. If you're having trouble identifying what the cause it, feel free to take a screenshot of the page and share it in the support channel on Discord.

If you identify a plugin as the cause of the memory leak, it's recommended to submit an issue to the developer of that plugin, as it's very unlikely Mythic Dungeons can do anything about it.

Sharing with a developer

If you are having trouble inspecting the heap dump yourself, you may share the heap dump on the issue tracker and a developer will attempt to inspect it when they have time.

[DEVELOPERS] How to not create memory leaks with your plugin

There are a few tricks to avoiding accidentally creating memory leaks due to the dynamic worlds that Bukkit/Spigot allows. Here's a little overview of things to watch out for.

Catch the "WorldUnloadEvent"!!

If you're tracking world data, stop tracking it when the world is unloaded! This is a VERY easy and simple way to make sure you don't keep holding on to worlds that no longer exists, and can be done just with a standard event handler provided by the Spigot API.

Avoid hard-referencing the World object!

If you reference a world object anywhere, that will keep it in memory. For example, if you have an object with a World field such as:
private World world;
The world will remain in memory until you set world to null.

A great way to combat this is to use something called a WeakReference. A weak reference will allow the world to still be unloaded from memory. Here's how to use it:

public class MyClass {
	// Here's our weak reference.
	private WeakReference<World> worldRef;
	
	public MyClass(World world) {
		worldRef = new WeakReference<>(world); // You create a WeakReference with `new` and pass the world as the constructor argument.
	}
	
	public World getWorld() {
		return worldRef.get(); // The `.get()` call returns the world stored at the weak reference. If the world is unloaded, it'll return null.
	}
}

Avoid hard-referencing entities!

Entities contain a hard reference to the World object! This will ALSO keep worlds alive in memory, so if you need to hold on to an entity, use a WeakReference<Entity>. (A WeakHashMap is also a good choice if you need a map, as it will allow the key to be unloaded.)

Watch out for Mythic Dungeons' own types

If you are writing an integration or a custom element, remember that some Mythic Dungeons objects are themselves a path to a world:

  • AbstractInstance holds a hard World field. Keeping an instance in a field of your own therefore keeps its world in memory for exactly as long as you keep the instance. Store the instance's UUID (getUuid()) and look the instance back up when you need it, or clear your reference when the dungeon ends.
  • Every dungeon element holds its instance. DungeonElement has an instance field, so holding a DungeonFunction, DungeonTrigger or TriggerCondition transitively holds a world too.
  • Anything you start in an element's onEnable() must be cancelled in its onDisable(). A repeating task whose body captures this keeps the element alive, which keeps the instance alive, which keeps the world alive. This is the single easiest way to leak a dungeon world from a custom element.

Mythic Dungeons uses the same techniques internally where it can: an instance's tracked entities live in a set backed by a WeakHashMap, precisely so that holding the entity set does not pin the world.

Updated Aug 19, 2026