# 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