Hello again,
I’ve just dug a bit deeper into workflows for analyzing Untracked Memory on Windows to confirm a few things, so I can share how a deeper analysis might look like on your side as well.
First off, this is highly platform dependent and this…
still holds (for a non comprehensive list of tools see this list). But lets dig into one such native platform tool for windows:
Windows Performance Recorder (WPR) and Windows Performance Analyzer (WPA).
First off, you’ll want to install the The Windows Assessment and Deployment Kit (ADK) which includes the latest version of these two tools (a just slightly older version failed to process the captured results for me so make sure you get something recent).
Next up, there is this guide on how to use the tools and e.g. grab the Symbol files for your Player (located at e.g. C:\Program Files\Unity\Hub\Editor\2022.3.46f1\Editor\Data\PlaybackEngines\windowsstandalonesupport\Variations\win64_player_development_mono in my case). It’s from 2018 but still holds up well for this.
In WPR you are mainly going to be interested in the VirtualAlloc usage profile so under the More Options foldout, make sure you’re recording that.
Use the Code Example for the MemoryProfiler.TakeSnapshot API to write yourself a small script that will take a snapshot and save it to a file. Log out the file location so you know where it’ll land.
Build a Player and quit other processes that you don’t want to capture with your analysis (like the Editor for example), start a capture in WPR and start your Player .exe. Get to the point where you want to check your memory usage, trigger a Memory Snapshot and quit the app (the easiest way to later determine around about where you took your snapshot). Stop WPR and safe the results to file, then open them in WPA.
As mentioned before, make sure to follow the guide for setting up the Symbol paths in WPA, then check the VirtualAlloc Commit LifeTimes analysis.
You’ll want to be filtering the data via the funnel icon. My filter query looked like this:
[Process Name]:~="2022" AND [Commit Stack]:~!"MemoryManager" AND [Decommit Time]:>"13,59s" AND [Commit Stack]:~!"UnityPlayer.dll!InitD3D11RenderDepthSurface/UnityPlayer.dll!CreateTextureResource" AND [Commit Stack]:~!"InitD3D11RenderColorSurface/UnityPlayer.dll!CreateTextureResource"
Where
- “2022” was enough to filter the processes down to my Unity Player executable
- “MemoryManager” applied to a symbolicated commit stack will remove all call stacks that involve Unity’s Native Memory Manager. For Unity 2022.3 or newer on relatively recent releases that should remove just about everything but Untracked and Graphics allocations.
- The other two filters are parts of our DirectX11 code where I doublechecked our code base that they do register their external memory with the Memory Manager. This registration happens in a callstack parallel to the actual call stack and is therefore harder to isolate.
- The Decommit Time filter filters out any allocation that is decommitted just before that sharp dip of memory usage at the termination of the Player, meaning only allocations that should still have been alive when taking the snapshot are included in the list.
There is every chance that there are other call-stacks that can be excluded as they might also register their external graphics memory. I just tested an Empty Project so far. The All of Memory table subtracts Graphics allocation sizes from the Untracked memory when in Allocated Memory mode by size, as the Memory Manager doesn’t actually know their address, but if you switch to the Memory Map page (only shown when enabled in the Memory Profiler Preference page) you can see all Untracked allocations by address.
While the Memory Map page does not attempt to do any mapping of tracked graphics resources to untracked allocations, you can try to get the overall amount of non-filteres allocations to about the amount of Untracked you see in the All Of Memory page and then use the Memory Map page to doublecheck the addresses of the remaining allocations to the ones the Memory Profiler lists as Untracked. Since the filter should really have caught any tracked native allocation except for Graphics this is where things get pretty fuzzy but maybe this helps regardless ![]()


