Skip to main content

Keep-Alive (Anti-GC)

Unreal's streaming manager keeps a hard reference to loaded assets only until the completion callback fires, then releases it. After that, if nothing else in your game holds a reference to the asset, the garbage collector is free to evict it.

If you load a mesh, use it temporarily, and then need it again later, it will be gone — causing another load, another hitch, and a brief pop-in.

The bRetainAfterLoad flag

The simplest way to keep an asset alive is to set bRetainAfterLoad = true in FAsyncKitLoadParams:

Params.bRetainAfterLoad = true

After the completion callback fires, the subsystem keeps an internal streaming handle alive for each asset. The asset cannot be GC'd until you explicitly release it.

Memory responsibility

bRetainAfterLoad is a memory trade-off. Retained assets stay in RAM indefinitely. Always call Release Assets when those assets are no longer needed (e.g., on level unload or when the owning Actor is destroyed).

Manual retain / release

For finer control, use the standalone Retain Assets and Release Assets functions:

On Begin Play  ──▶ Retain Assets [/Game/Meshes/Rock]
On End Play ──▶ Release Assets [/Game/Meshes/Rock]

Ref-counting

The retain system is ref-counted. You can safely call Retain Assets on the same asset from multiple systems — each call increments the counter. The asset is only released when the counter reaches zero.

System A: Retain [Rock]    → ref count = 1
System B: Retain [Rock] → ref count = 2
System A: Release [Rock] → ref count = 1 (still retained)
System B: Release [Rock] → ref count = 0 (released, GC may collect)

This prevents one system from pulling the rug out from under another.

Release all

To release every retained asset at once (useful on level transition):

Release All Retained

Handle-level release

If you stored a UAsyncKitHandle, you can call Release on it. This drops the subsystem's GC-preventing pin on the handle itself — it does not cancel the load, and does not release any bRetainAfterLoad assets. Use it only when you no longer need to track the handle.

To abort the load entirely, use Cancel instead.