High memory usage and persistent memory footprint when loading large 3D Tilesets – removal doesn’t free memory (Unity 6000.3.5f2)

Hi everyone,

I’m using Cesium for Unity with Unity 6000.3.5f2 (the latest Unity 6 preview) and loading large 3D Tileset services (e.g., detailed city models with millions of triangles). I’m facing severe memory issues:

  • Memory usage (both managed and native) skyrockets during tileset loading.

  • Even after removing the tileset from the scene, memory does not drop back to its baseline. Over time, loading/unloading multiple tilesets causes out‑of‑memory crashes.

  • I’ve noticed that when I drag the camera to load multiple tile regions, the number of loaded objects (tiles) keeps increasing steadily. Setting maximumCachedBytes to a low value does not prevent this continuous growth – the tile count and memory usage keep rising regardless.

  • Calling my removal method (see below) does not immediately reduce memory usage; the profiler still shows large native allocations for textures and geometry buffers.

Here is the removal code I’m currently using:

csharp

Cesium3DTileset tileset = threeDTilesLayerDic[data.layerIds[i]];
threeDTilesLayerDic.Remove(data.layerIds[i]);
overlayLevelRanges.Remove(data.layerIds[i]);

if (tileset == null) return;
tileset.suspendUpdate = true;
tileset.maximumSimultaneousTileLoads = 0;
tileset.maximumCachedBytes = 0;
tileset.Dispose();
Destroy(tileset.gameObject);

I expected that setting maximumCachedBytes = 0, calling Dispose(), and destroying the GameObject would release all GPU and CPU resources, but that doesn’t seem to happen.

Attached screenshots:

  • Figure 1 – Memory profiler snapshot showing the high memory usage after loading a large tileset.

  • Figure 2 – The progressively increasing tile/object count as I pan/zoom over different regions, despite maximumCachedBytes being set low.

My questions:

  1. Is there a recommended, reliable way to completely unload a Cesium3DTileset and free all its associated resources (native textures, vertex buffers, etc.)?

  2. Does Dispose() fully release native resources, or do I need to call additional cleanup methods (e.g., Resources.UnloadUnusedAssets(), GC.Collect(), or manually destroy materials/meshes)?

  3. Is my disposal sequence correct? Should I disable the tileset’s update loop before disposal, and is there any specific order to follow?

  4. Why does maximumCachedBytes not cap the memory growth effectively when panning over new areas? Are tiles being retained in some internal cache that ignores this setting?

  5. Is the delayed memory release expected due to Unity’s resource management, or is there a known bug in Cesium for Unity with Unity 6000? If so, are there workarounds?

I’m using the latest Cesium for Unity version (as of June 2026). Any insights, code examples, or configuration tweaks would be hugely appreciated. Thank you!

Hi @jh007 ,

Thank you for sharing such a detailed explanation! It sounds like you’re running into challenges with memory management and resource cleanup when working with large 3D Tilesets in Cesium for Unity. Let me address your questions and provide some suggestions:

1. Reliable way to unload a `Cesium3DTileset`: Your current implementation of setting `maximumCachedBytes = 0`, `Dispose()`, and `Destroy()` should generally work to release Cesium resources. That said, Unity may still retain certain resources like textures and meshes in memory due to its internal resource management. To ensure these are properly released, you can try calling [Resources.UnloadUnusedAssets] ( Unity - Scripting API: Resources.UnloadUnusedAssets ) after destroying the GameObject. This method forces Unity to unload any unused assets from memory. Additionally, invoking `GC.Collect()` can help trigger garbage collection.

2. Does `Dispose()` release all native resources?: `Dispose()` should release the resources allocated by Cesium’s internals, but Unity’s native asset lifecycle management may still hold onto certain GPU resources (e.g., textures and meshes allocated for tiles). Combining Cesium cleanup methods with Unity’s resource-management tools (e.g., `UnloadUnusedAssets`) and monitoring with the Unity Profiler can help determine if any unused assets remain.

3. Disposal sequence: Pre-disabling the tileset’s update loop (via `suspendUpdate = true`) is a good step to prevent further resource loading during cleanup. Your sequence is mostly correct, though you could experiment with the sequence of calling `Destroy()` vs. `Dispose()` to ensure proper teardown. For example, you might try calling `Dispose()` last, after removing the tileset and destroying the GameObject.

4. Behavior of `maximumCachedBytes`: This setting should limit the amount of memory allocated to cached tiles, but depending on your scene, panning to new areas will still load additional tiles temporarily. This may suggest either excessive pre-fetching behavior or a potential bug. Would you be able to provide your configuration for the `Cesium3DTileset` component and any steps to reproduce this issue?

5. Delayed memory release: Delays can occur due to Unity’s garbage collection and asset reference system. Even if Cesium releases its native allocations, residual Unity resources may not be immediately freed. This behavior is likely exacerbated by the large volume of assets in your tilesets. Have you tried testing this on Unity 6 LTS? Preview versions (like 6000.3.x) could have memory management quirks.

If none of the above resolves your issue, I’d recommend testing with a smaller tileset and sharing your results. Meanwhile, I’ll flag your experience for the Cesium for Unity team in case there’s an unreported issue with the memory cleanup implementation or a conflict with Unity 6000.3.x. If possible, sharing a minimal reproducible example project or steps would also help us investigate.

Looking forward to your insights!

Best regards,
Jake

Hi @Jake ,

Thank you for your detailed response! Let me provide more information regarding point 4 and the memory cleanup follow-up.

Regarding maximumCachedBytes behavior:

Our 3D Tileset service contains city-level model data (millions of triangles across the entire city). During testing, I continuously pan and zoom the camera across different city areas. What I observed in the Unity hierarchy is that previously loaded tile GameObjects are only ever deactivated (hidden), never destroyed. Even after moving far away from a tile region and exceeding the maximumCachedBytes threshold, these old tiles remain in the scene hierarchy. This strongly suggests that the tile eviction mechanism is not actually releasing/destroying tiles — it is only deactivating them. This would explain the continuously growing memory footprint regardless of the maximumCachedBytes setting.

Here is our Cesium3DTileset configuration for reference:

  • maximumCachedBytes: tested with values ranging from 0 to a small threshold
  • maximumSimultaneousTileLoads: default value
  • Other settings: default values

Steps to reproduce:

  1. 1.Load a city-level 3D Tileset.
  2. 2.Pan the camera across different city areas to trigger loading of new tile regions.
  3. 3.Observe the hierarchy — previously loaded tiles remain as deactivated GameObjects and are never destroyed.
  4. 4.Monitor memory via Unity Profiler — native memory (textures, meshes) keeps growing and never decreases.

Regarding Resources.UnloadUnusedAssets() + GC.Collect() effectiveness:

I tried calling both Resources.UnloadUnusedAssets() and GC.Collect() after tileset removal as suggested, but the memory did not drop significantly. Based on my analysis, the likely reasons are:

    Cesium Native (C++) managed memory is invisible to Unity’s resource system. The vertex buffers, index buffers, and decoded texture pixel buffers allocated by Cesium’s underlying C++ layer are not registered as Unity Assets, so Resources.UnloadUnusedAssets() cannot track or release them.

    Residual reference chains. Even if Unity-created Texture2D and Mesh objects exist, references from Materials, event callbacks, or static containers can prevent them from being flagged as “unused” by Resources.UnloadUnusedAssets().

    Delayed GPU resource release. GPU-side resources (VRAM) are not freed immediately — Unity waits until the rendering pipeline has finished using them, which causes additional delay.

    Managed heap is only a small fraction.GC.Collect() only reclaims C# managed objects, which typically represent less than 10-15% of the total memory in a tile-heavy scene. The dominant cost is in native textures and geometry buffers.

I also captured Memory Profiler snapshots before and after loading/unloading — the native Object diff shows that Texture2D and Mesh assets created by Cesium tile loading persist even after calling Dispose() + Destroy() + UnloadUnusedAssets() + GC.Collect().

Could you confirm whether Dispose() is expected to fully destroy all tile sub-objects (child GameObjects with Mesh/Texture components), or does it only release Cesium’s internal references? If child GameObjects are expected to survive disposal, that would explain the persistent memory. Are there any additional API calls needed to trigger a complete teardown of all loaded tile content? Thanks again for the help!