← Back to Tutorials
Intermediate~16 min readUE 5.5 / 5.6

Memory Insights & Low-Level Memory Tracker

You need three tools to triage UE5 memory: memreport for a UObject-centric snapshot, LLM (Low-Level Memory Tracker) for an OS-allocation-centric continuous view, and Memory Insights to stitch both onto a timeline. Per Epic engineer Sebastian Thomeczek, untracked memory in Insights is allocator overhead and driver bytes Insights cannot see — the gap between process RSS and Insights total is real and important. This tutorial walks the senior workflow.

1

Why three tools, not one

Each tool answers a different question:

  • memreport — UObject-centric snapshot. "What objects are loaded right now and how big are they?"
  • LLM — OS-allocation-centric continuous view, tagged by subsystem. "Where is my heap going by category?"
  • Memory Insights — timeline + callstack of every allocation. "What allocated this byte and when?"

Senior devs need all three because allocator overhead, driver memory, and untracked LLM bytes diverge from process RSS — and you can't tell which from any single tool.

2

Capturing a Memory Insights trace

Launch a Development build with memory channels
YourGame.exe ^
    -trace=default,memory,callstack,module,memalloc,memtag,metadata,assetmetadata,bookmark ^
    -statnamedevents
memalloc must be on at process start You cannot toggle it later via Trace.Start. Forgetting this produces an empty Memory tab with timing data still showing — the most common Memory Insights trap.

The memalloc channel is heavy — expect ~10–20% perf overhead during capture. Use Development/DebugGame builds, not Shipping. LLM is compiled out of Test/Shipping by default unless ENABLE_LOW_LEVEL_MEM_TRACKER=1 is force-defined.

3

Reading the Memory Insights timeline

Four panels that matter:

  • Timing — main timeline.
  • Investigation — A/B markers and query rules.
  • LLM Tags — per-tag totals over time.
  • Modules — allocations by code module.

Three numbers to know: LLM Total, LLM TrackedTotal, LLM Untracked. Per Epic's Sebastian Thomeczek, untracked = allocator overhead + driver bytes Insights cannot see. A growing Untracked over time is a smell, not necessarily a leak.

4

The Investigation panel as a leak hunter

The leak-hunting workflow:

  1. Drag A/B markers on the timeline around the suspected leak window (e.g., "before level load A → after level B").
  2. Pick a query rule: live-at-B-but-not-at-A, long-living, short-lived-leaked.
  3. Run Query.
  4. Sort the Alloc table by Size or LLM Tag.
  5. Double-click into the callstack — that's your leak source.

Iteration: bookmark with TRACE_BOOKMARK(TEXT("PreLoad"))obj gcTRACE_BOOKMARK(TEXT("PostUnload")) around the suspect transition. Three load/unload cycles; query between matched bookmarks; any tag still growing across cycles is the leak.

5

LLM — stat llm & stat llmfull

LLM exposes two trackers (Default and Platform), each with its own tag stack. Run with -llm at launch (Development/DebugGame; not Shipping by default), then:

  • stat llm — live, grouped tag totals on screen.
  • stat llmfull — every tag, including platform-specific ones (RHIMisc, Audio, Mesh, Texture, Animation, Physics, Niagara, EngineMisc, UObject, AssetRegistry).
  • -llmcsv writes CSV per-tracker into Saved/Profiling/llm/, one column per tag, MB values, ~5s cadence. Diff CSVs across builds for tag-level regression detection.
6

Custom LLM tags for game code

YourSubsystem.h
// Header
LLM_DECLARE_TAG(MyGame_AI);

// .cpp
LLM_DEFINE_TAG(MyGame_AI);

// Wrap allocations
{
    LLM_SCOPE_BYTAG(MyGame_AI);
    AICachedData.Reserve(LargeNumber);
    // or LLM_SCOPE(ELLMTag::Foo) for built-in tags
}

Custom tags appear in both stat llmfull and the Memory Insights LLM Tags panel without engine modification. Add tags around any subsystem you ship more than ~5 MB through — AI caches, replay buffers, custom asset systems, networking subobject pools.

7

memreport deep dive

Trigger memreport -full in PIE or via gameplay command. File lands in <Project>/Saved/Profiling/MemReports/, named with map + timestamp. Walk the canonical sections:

  • Platform memory header — OS-side numbers.
  • obj list -alphasortNumKBytes (UObject body) and ExclusiveResKBytes (owned resources).
  • rhi.DumpMemory — GPU-side resources.
  • ListTextures, ListSounds, r.DumpRenderTargetPoolMemory — per-asset-class.

BaseEngine.ini has [MemReportCommands] and [MemReportFullCommands] sections that you can extend with project-specific dumps. Add anything subsystem-specific you want to track.

Editor inflates AnimSequence sizes ~2× Per Tom Looman, editor builds inflate AnimSequence and similar asset sizes vs cooked. Always profile a Shipping/Test cook for "real" memory numbers; editor numbers are a directional indicator, not a budget.
8

GC bookmarks for leak repro

The deterministic leak-repro pattern:

C++ scenario driver
TRACE_BOOKMARK(TEXT("PreLoad_Cycle1"));
LoadLevel(TEXT("TestLevel"));
ProcessAsyncLoading();
TRACE_BOOKMARK(TEXT("PostLoad_Cycle1"));

UnloadLevel(TEXT("TestLevel"));
GEngine->ForceGarbageCollection(true);
TRACE_BOOKMARK(TEXT("PostUnload_Cycle1"));

// Repeat for cycles 2 and 3

After three cycles, run an Investigation query between matched bookmarks (e.g., PostUnload_Cycle1 vs PostUnload_Cycle3). Any tag still growing across cycles is the leak. Pair with obj gc or gc.CollectGarbageEveryFrame 1 for aggressive GC pressure.

9

UE 5.6 asset-class memory profiling

UE 5.6 added experimental LLM TagSetsSystem, AssetClass, Asset — with per-platform memory budgets per asset type (per Tom Looman's 5.6 highlights). Switch trackers via au.MetaSound.Pages.SetTarget-equivalent CVars or via the LLM TagSet config.

Workflow: enable asset memory tracing, switch the LLM TagSet to AssetClass, set per-platform budgets per asset type. Out-of-budget rows light up automatically in Insights.

This is the closest UE5 has gotten to "automatic per-asset-type budget enforcement" — useful but still experimental in 5.6.

Senior workflow checklist

  1. Repro the symptom. OOM crash? Streaming pool warning? Slow growth?
  2. Bookmark. Surround the suspect window with TRACE_BOOKMARK.
  3. Capture trace with the memalloc channel on at process start.
  4. Diff via Investigation panel. A/B between matched bookmarks.
  5. Confirm with memreport -full. Two snapshots, diff side-by-side.
  6. Add a custom LLM tag for the offending subsystem.
  7. Re-run to verify the fix.

PerfGuard can launch the test scenario with -trace=default,memory,memalloc,memtag,metadata,assetmetadata,bookmark + -llm -llmcsv, parse the per-LLM-tag CSV, and emit per-tag delta vs baseline alongside CPU/GPU regressions. UE 5.6 AssetClass-level deltas surface in the same regression report — flag a StaticMesh or Texture budget regression with the same red/green PASS/FAIL UX as a CPU spike.