Hi Everyone,
So, I think either I’m doing something very wrong, or I fundamentally misunderstand how unity manages memory, in either case I’d really value your insight.
My goal is very simple, I want to:
- Snapshot ram usage on an empty scene
- Load an additive scene with a textured FBX in it
- Unload the additive scene and FBX
- Snapshot ram usage again and, critically, have RAM usage be returned to around about the same level as it was in step 1.
However, I can’t for the life of me manage to achieve this simple goal.
My Setup
Hardware:
Up to date Windows 10 machine, Intel Processor, Nvidia graphics card.
Software:
Unity 6000.0.25f1, Built-In Render Pipeline
Memory Profiler 1.1.3 (com.unity.memoryprofiler)
Scenes:
RunThisScene – My starting scene, just a camera and my “RunThis” script.
ModelScene1 – Just the FBX (textured) and a directional light
EmptyScene 1 – Completely empty, just exists to test the statement that assets unload with a one scene delay from this thread: What on earth is "Reserved" memory and why is killing me? - #18 by Ikaro881
Script:
using System;
using System.Collections;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using Unity.Profiling.Memory;
using UnityEngine.SceneManagement;
public class RunThis : MonoBehaviour {
const string MODEL_SCENE_1 = "ModelScene1";
const string EMPTY_SCENE_1 = "EmptyScene1";
void Start() {
Demonstration();
}
async void Demonstration () {
await Task.Delay(2000);
await SaveMemorySnapshot(1);
await Task.Delay(2000);
await LoadScene(MODEL_SCENE_1);
await Task.Delay(5000);
await UnloadScene(MODEL_SCENE_1);
await Task.Delay(2000);
await ForcedMemoryCleanup();
await Task.Delay(2000);
await SaveMemorySnapshot(2);
await Task.Delay(2000);
await LoadScene(EMPTY_SCENE_1);
await Task.Delay(5000);
await UnloadScene(EMPTY_SCENE_1);
await Task.Delay(2000);
await ForcedMemoryCleanup();
await Task.Delay(2000);
await SaveMemorySnapshot(3);
}
async Task SaveMemorySnapshot (int number) {
bool snapshotTaken = false;
MemoryProfiler.TakeSnapshot(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), string.Format("{0}_MemorySnapshot{1}.snap", DateTime.Now.ToString("yyyy-MM-dd_hh-mm"),number)), delegate (string filepath, bool success) {
if (success == false) {
Debug.LogError("Failed to take memory snapshot " + number);
}
snapshotTaken = true;
}, CaptureFlags.ManagedObjects | CaptureFlags.NativeObjects | CaptureFlags.NativeAllocations | CaptureFlags.NativeAllocationSites | CaptureFlags.NativeStackTraces );
while (snapshotTaken == false) {
await Task.Delay(1);
}
}
async Task LoadScene (string sceneName) {
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
while (!asyncLoad.isDone) {
await Task.Delay(1);
}
Scene disposableScene = SceneManager.GetSceneByName(sceneName);
SceneManager.SetActiveScene(disposableScene);
}
async Task UnloadScene (string sceneName) {
AsyncOperation asyncUnload = SceneManager.UnloadSceneAsync(sceneName, UnloadSceneOptions.UnloadAllEmbeddedSceneObjects);
while (!asyncUnload.isDone) {
await Task.Delay(1);
}
}
async Task ForcedMemoryCleanup () {
// Wait a single frame for any existing destroys unity has queued to have completed
await Task.Delay(1);
bool unusedAssetsUnloaded = false;
StartCoroutine(UnloadUnusedAssetsCoroutine(delegate () {
unusedAssetsUnloaded = true;
}));
while (unusedAssetsUnloaded == false) {
await Task.Delay(1);
}
GC.Collect();
}
IEnumerator UnloadUnusedAssetsCoroutine (Action callback) {
yield return Resources.UnloadUnusedAssets();
callback();
}
}
Memory Profiler Results
All snapshots are taken from a windows development build. I’m trying to get my head round it there before I complicate things by going back to iOS/iPadOS.
Please see the script above for context on when the snapshots below were taken.
Things I don’t understand
- Why has resident memory still increased by 30.9MB? I’m working on an iPad application which needs to load multiple models in a sequence on devices with only 2GB-3GBs of ram, we can’t afford to keep leaking memory every time we change model.
- In the final comparison why is the “Diff” in “Total Resident on Device" 30.9 MB, but the combined Size Difference in the “All of Memory” Tab less then 16MB? Is “All Of Memory” actually just some of memory?
- The empty scene load+unload seems to clear around 16MB of untracked memory while marginally increasing tracked memory use… Yet resident memory AKA the “Crashy Memory” I predominantly care about, only goes up, why?
- Does iOS/iPad even have non-resident memory? I have a terrible feeling it might not leverage hard drive space and ultimately, I should be worrying more about total allocated memory regardless.
A thousand thank yous to anyone who read through all that.






