# 3. Memory Profiler Snapshots Part of [[Profiling and Optimization]]. Previous: [[2. CPU Allocations and Incremental GC]]. Next: [[4. Frame Debugger and GPU]]. > [!note] Verification status > Package versions from the 6000.5 released-package page: **1.1.12** is "released for" 6000.5; **1.2.0** (and 1.2.0-pre.1) are also listed as available. Workflow below is the standard capture / compare / Unity Objects / references path documented by the package. Some UI labels (Impact, Shortest Path To Root) were added or fixed in the 1.1.x changelog — they may differ slightly on 1.2.0. **No snapshot was captured for this write-up.** ## 3.1 Install and open Package Manager: `com.unity.memoryprofiler`. Use **1.1.12 or newer**. Open **Window > Analysis > Memory Profiler** (the entry lives with the other Analysis windows; if your version names it slightly differently, use the package's Window menu). This is not the Memory *module* in the Profiler window. The module is a graph. The Memory Profiler package is a **snapshot of everything allocated** at a moment in time, including why it is still alive. ## 3.2 Capture a useful pair Memory is a comparison sport. 1. Start a Development Build (or Play Mode, for a first look). 2. Get to a stable, boring state — main menu, or standing in the first room. **Capture A**. 3. Perform the verb: load a level, open and close the inventory 20 times, play a fight, return to the room. **Capture B**. 4. Compare B against A. If B is bigger and stays bigger after you have returned to the same screen, you may have a leak (an object still rooted). If B spikes and comes back, you have a peak, which is a budget problem, not necessarily a leak. On device, capture from the Memory Profiler window attached to the Player the same way the Profiler attaches. Quest and Android captures are the ones that matter for those platforms; Editor snapshots include Editor-only objects and will scare students unnecessarily. ## 3.3 Managed vs native Unity memory is two heaps in conversation: - **Managed** — C# objects, the ones the GC later collects. A `List<Enemy>`, a closure, a string. - **Native** — engine objects: meshes, textures, audio clips, the native side of a `UnityEngine.Object`. These die when you `Destroy` the object or unload the asset/bundle, not when a C# reference falls out of scope *if the native object is still alive* — and the inverse is also a common bug (a destroyed Unity object whose managed wrapper is still in a list). A texture that is 4K, uncompressed, and used once in a distant cutscene is usually a **native** budget problem. A list of strings you build every frame is a **managed** problem (and a GC problem, part 2). ## 3.4 How to read a snapshot Start here, in roughly this order: 1. **Summary / memory map** — is the capture even in the right order of magnitude for the platform? (Quest 2 has a tight RAM budget; a 3 GB Editor snapshot does not translate.) 2. **Unity Objects** — textures, meshes, RenderTextures, materials, GameObjects. Sort by size. Look for duplicates (the same texture imported twice, or two copies because a prefab and a scene both embedded it). The 1.1.x notes include a duplicates view; use it. 3. **Allocated vs resident** (when the package offers the breakdown) — resident is what is actually in physical memory. On mobile/Quest, resident is the number you feel. 4. **References / Shortest Path To Root** — why is this 64 MB texture still alive? A static field, a DontDestroyOnLoad manager, an Addressables handle you never released, a closed UI document that is only `display: none`. Walk the path; fix the root, not the leaf. 5. **Managed allocations** — types with many instances. `byte[]`, strings, temporary collections. If you use Addressables (the Addressables tutorial in this batch), unreleased handles are a classic root. If you use `[SerializeField]` references on a DontDestroyOnLoad singleton, those assets stay in memory for the whole run. ## 3.5 A teaching leak Write a manager that loads a portrait texture with `Resources.Load` (or Addressables) every time you talk to an NPC, stores it in a static list "for later," and never unloads it. Capture A at the start of a conversation loop, capture B after 20 conversations. Then fix it: load/release per conversation, or share one cached texture per id. This is the Memory Profiler in one lab. ## Official docs - Memory Profiler package (6000.5): https://docs.unity.com/en-us/engine/6000.5/manual/packages-list/packages-all/pack-safe/com-unity-memoryprofiler - Memory Profiler 1.1 changelog: https://docs.unity3d.com/Packages/[email protected]/changelog/CHANGELOG.html