AssetManager: Dependent assets remain loaded after primary asset is unloaded
0 reputation · 28 Jun 2021, 22:59 UTC
0 reputation · 28 Jun 2021, 22:59 UTC
When an asset that references other assets is unloaded via AssetManager.unload(), the dependent assets continue to occupy GPU and RAM memory, even though their reference counts drop to zero.
AssetManager uses a global reference counter but does not traverse the dependency graph on unload. The API is identical across desktop, Android, iOS, and WebGL, and no configuration flag exists to alter this behavior.
35275 reputation · 29 Jun 2021, 02:19 UTC
AssetManager’s unload() only decrements the reference count of the named asset. It does not traverse the dependency graph, so any assets that were loaded as dependencies (textures, shaders, models referenced by the primary asset) keep their reference counts unchanged and remain in GPU/RAM memory. This is the documented, cross‑platform behavior in LibGDX 1.9.x through 1.12.x.
unload(asset) reduces that counter by one.AssetManager.getDependencies(assetName) returns direct dependencies, enabling a manual recursive unload.assetmanager.autoUnloadDependencies) exists in any stable release.There is no hidden flag today. LibGDX 1.13.0 (unreleased as of 2026‑10) may introduce an optional recursiveUnload parameter, but you should not rely on it. The core team has avoided a default auto‑unload because shared dependencies (e.g., two scenes using the same texture) would be incorrectly released when the first parent unloads.
Making recursive unload the default would break existing projects that share assets across multiple parents. A safe design would require an opt‑in mode (e.g., unload(asset, true)) or explicit ownership semantics, preserving current behavior for existing code.
AssetManager.dispose() at screen transitions to force a full cleanup of all loaded assets.unload() on each dependent to free GPU textures via GL.deleteTexture; browser GC timing may still delay reclamation.void unloadRecursive(AssetManager mgr, String asset) {
for (String dep : mgr.getDependencies(asset)) {
unloadRecursive(mgr, dep);
}
mgr.unload(asset);
}
Call unloadRecursive(mgr, primaryAsset) instead of a single unload(). Caution: This will over‑release any asset that is shared by multiple parents. Audit your dependency graph first.
Enable debug mode and log reference counts before and after unload:
mgr.setDebugMode(true);
System.out.println("Before: " + mgr.getReferenceCount(dependentAsset));
mgr.unload(primaryAsset);
System.out.println("After: " + mgr.getReferenceCount(dependentAsset));
// Expect count > 0 if dependent is not explicitly unloaded
If the count stays above zero, the dependent is still loaded (expected). If it drops to zero, something else released it.
Are any of the dependent assets shared by multiple primary assets? If yes, a blanket recursive unload will break the other parents. In that case, implement a custom ownership graph (e.g., a Scene class that tracks its exclusive assets and disposes them in its own dispose() method) instead of using the simple recursive helper.
Use comments to ask for clarification. Post a solution as an answer.
7,010 reputation · 29 Jun 2021, 03:12 UTC
Reference-counted asset managers typically treat each asset as an independent node; unload() only decrements the direct target's count and never walks outgoing edges. The practical pattern is to introduce a lightweight asset bundle or scene context object that records the full dependency set when the root asset is loaded. On teardown the bundle iterates that set and calls release() on each dependency before releasing the root, guaranteeing deterministic GPU/CPU reclamation without relying on a global GC pass.
On WebGL and mobile, remember that driver memory is only returned to the OS when the context is lost; explicit dispose()/gl.deleteTexture() calls inside the bundle's teardown are necessary for immediate release. A backward-compatible auto-unload feature would need an opt-in flag (e.g., unloadDependencies: true) because existing code often shares dependencies across multiple root assets.