How to dispose UObject after using it in the editor.

Hello guys,

I’m experiencing a lot of issues with the UE4 memory management system. I have a couple of C++ scripts in my editor to process StaticMesh such as : Generating LODs, Collision testing and more. I’m processing up to 4000 huge static meshes through a loop but i’m not able to free memory after one is processed.

I tried a lot of things from this very cool tutorial : Assets Streaming

I’m out of RAM after ~400 processes because the meshes are still referenced in memory. I can’t use delete from naked C++ and what i’m trying to do is: Unload asset and call the garbage collector but it doesn’t seem to work.

Here is my c++ code. Thanks in advance :slight_smile:

FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();

// prepare filter
FARFilter Filter;
Filter.ClassNames.Add(UStaticMesh::StaticClass()->GetFName());
Filter.PackagePaths.Add(FName(FString::Printf(TEXT("%s"), *PathToEditorAssetsDirectory)));
Filter.bRecursivePaths = true;

// get assets without loading.
TArray<FAssetData> MeshAssetList;
AssetRegistry.GetAssets(Filter, MeshAssetList);

FStreamableManager StreamableManager;

for (FAssetData Data : MeshAssetList)
{			
     TSharedPtr<FStreamableHandle> Handle = 
     StreamableManager.RequestSyncLoad(Data.ToSoftObjectPath());
					
     UStaticMesh* Mesh = Cast<UStaticMesh>(Handle->GetLoadedAsset());
     check(Mesh);

   // use static mesh here !!!

   // try to dispose it here to free memory.
   StreamableManager.Unload(Data.ToSoftObjectPath());
   CollectGarbage(EObjectFlags::RF_NoFlags);
}

Here are a few techniques that have worked for me when doing large‐batch StaticMesh processing in the editor, plus a tool suggestion at the end to help you keep memory‐budget and cleanup tasks tied directly to each asset:


1. Break and Release the Handle

Your TSharedPtr<FStreamableHandle> is pinning the asset in memory until it’s reset or released. After you’re done with each mesh:

cpp

// …after processing…
Handle->ReleaseHandle();    // tell the StreamableManager you’re done
Handle.Reset();            // drop the shared pointer

That ensures there are no lingering strong references keeping the mesh alive.


2. Mark the UObject for GC

Even once the handle is released, the UObject may still be in the root set. Right after resetting your handle, do:

if (Mesh)
{
    Mesh->MarkPendingKill();      // flag it for GC
    Mesh = nullptr;               // clear your raw pointer
}
CollectGarbage(RF_NoFlags);      // run GC immediately

This combination makes sure Unreal’s garbage collector sees the asset as collectible.


3. Unload the Package (Optional)

For extremely large meshes, you can also explicitly unload the package:

UPackage* Package = Mesh->GetOutermost();
if (Package)
{
    TArray<UPackage*> ToUnload = { Package };
    FPackageName::UnloadPackages(ToUnload);
}
CollectGarbage(RF_NoFlags);

That drops any remaining package‐level data.


4. Flush Async Loading

If you ever switch to async loads, call:

FlushAsyncLoading();

right after CollectGarbage to ensure no async callbacks hold references.

All these steps work—but they can be easy to forget in the heat of a 4,000-mesh batch. I’ve found it invaluable to attach a quick “Memory Cleanup” checklist to each StaticMesh asset so that anyone on the team sees exactly what GC steps need to run before moving on.

If you’d like to automate that and tie checklists, comments, and even memory-budget tags directly to each asset inside the editor, check out Asset Optics. It lets you:

  • Pin a “Clear GC handle ➔ MarkPendingKill ➔ CollectGarbage” checklist to every mesh.
  • Track per-asset memory-budget metadata (so you never exceed your RAM cap).
  • View and manage all your cleanup tasks in a live dashboard.

You can learn more here:
:link: Asset Optics – Next-Gen Asset Management | Fab

Hope that helps you clear out those 400-mesh batches without running out of RAM!