Quick answer: Store the TSharedPtr<FStreamableHandle> in your class. Call Handle->CancelHandle() to cancel a load and release its reference. Without this, partially-loaded assets stay in memory.

A level loader uses StreamableManager to async-load level assets. If the player backs out before load completes, you discard the result — but Memreport shows the partially-loaded assets persist long after. Each level retry adds to the leak.

What FStreamableHandle Tracks

StreamableManager.RequestAsyncLoad returns a TSharedPtr<FStreamableHandle>. The handle:

If you discard the TSharedPtr while load is in progress, the load continues (the StreamableManager keeps a reference internally) and the assets stay loaded indefinitely.

The Fix

class ALevelLoader : public AActor
{
    TSharedPtr<FStreamableHandle> CurrentLoad;

public:
    void StartLoad() {
        UAssetManager& AM = UAssetManager::Get();
        CurrentLoad = AM.LoadAssetList(AssetPaths, FStreamableDelegate::CreateUObject(this, &ThisClass::OnLoaded));
    }

    void AbortLoad() {
        if (CurrentLoad.IsValid()) {
            CurrentLoad->CancelHandle();
            CurrentLoad.Reset();
        }
    }
};

CancelHandle stops the load and drops the handle’s asset references. Reset on the TSharedPtr releases the handle itself. Next GC pass, the partially-loaded assets become eligible for collection.

Verify with Memreport

Console: memreport -full. Search the output for your asset class names. After AbortLoad + GC tick, the count should drop. If unchanged, you have another reference holding them — check soft references, ObjectIterator usage, etc.

Force GC for testing: obj gc in console.

Auto-Cancel Pattern

For per-actor loads that should cancel on actor destruction:

virtual void EndPlay(const EEndPlayReason::Type Reason) override
{
    if (CurrentLoad.IsValid()) {
        CurrentLoad->CancelHandle();
    }
    Super::EndPlay(Reason);
}

Actor leaves the world → pending loads cancel. Prevents callbacks firing on a destroyed actor and frees memory.

Don’t Cancel Completed Handles

If the load already completed, calling CancelHandle is a no-op but the handle still holds references. To free completed loads, use ReleaseHandle instead.

Verifying

Trigger a load, immediately AbortLoad. Run obj gc and memreport. The assets should not appear. Run a stress test loading and aborting 100 times in a loop — memory should stay flat.

“Hold the handle; cancel the handle. Discarding the TSharedPtr without canceling leaves assets in memory.”

Every async load should have a paired cancel path. Treat partially-loaded assets as resources that need explicit cleanup.