Skip to main content

Memory

A explanation of the few different memory management systems in Unreal Engine: Garbage Collection, Smart Pointers, alias Standard C++ Memory Management.

Overview

Unreals game-level memory management system uses reflection to implement garbage collection. Working effectively in Unreal requires some understanding of how these two systems interact.

What is reflection?

Inform yourself about Unreals (Reflection) Property System and how to use it. Reflection allows the engine to determine if objects are still referenced by other objects, making garbage collection a viable strategy for managing memory.

Garbage Collection

Concept

One of the most important tasks within a game engine is managing memory. Unreals approach to solve this problem is the usage of garbage collection. In this approach, the engine will automatically delete objects when they are no longer needed. An object is no longer needed when it is no longer referenced by any other object. If you are more curious about Garbage Collection, visit garbage collection–computer science.

Unreal uses the reflection system to drive garbage collection. Because the engine knows about your objects and properties, it can recognize when an object is no longer needed and automatically delete it.

While this automatic memory management does significantly reduce the mental workload to working in the engine, it is important to understand at a high level how it works, as it can only work well when you follow the rules.

  • Declaration UPROPERTY
    Its recommended to declare evey member of a class UPROPERTY, if an member is left naked Unreal Engine won't know about it. So, an object you are pointing at could get deleted out from under you! It's safe to leave value types such as an int or a bool naked although they could not be saved, replicated, or appear in the editor.

  • TArray
    TArray is the only container that is safe to have pointers in

Usage

Garbage Collection (UObject*, TWeakObjectPtr, TSoftObjectPtr) is a automatic memory management system exclusive for Unreal Objects that tracks UObject sub-classes, which include AActor and UActorComponent. Unreal Engine will automatically add newly created UObjects to its internal objects list, so even with improper use it's not easy to have memory leaks, but it is easy to cause crashes.

UObjects should never be created with new, but only with the default creation methods (NewObject, SpawnActor, CreateDefaultSubobject)

Objects are primarily kept alive in 3 ways:

  • By having a strong reference (UPROPERTY) to them (from objects that are also referenced)

  • By calling UObject::AddReferencedObjects (from objects that are also referenced)

  • By adding them to the root set with UObject::AddToRoot (typically unnecessary)

When objects do not fulfill any of the above conditions, on the next GC cycle they will be marked as unreachable and garbage collected (destroyed). Passing an object as an Outer to another object does not automatically mean the other object will be kept alive, the same goes for default subobjects. To force the destruction of objects that are still reachable, you can call MarkPendingKill on them, and it will force their destruction on the next GC cycle.

Destruction of an object doesn't necessarily all happen in the same frame, when garbage collection starts on it, it will first call BeginDestroy (do not call this yourself), then, when ready FinishDestroy. The GC runs in the game thread so you can trust that it won't clean up an object within the lifetime of a function. While the most common way of keeping objects alive is through a UPROPERTY, actors and components work differently:

Levels reference their actors and actors reference their components. Both work by overriding the UObject::AddReferencedObjects implementation and collecting what they do not want to be garbage collected. This means that even if there are no strong references to level actors and components, they won't be garbage collected until manually destroyed, or their level is unloaded.

Garbage collector will automatically clear the following references to garbage collected objects:

  • Raw pointers declared with UPROPERTY, will be set to nullptr

  • Raw pointers in UObject compatible containers declared with UPROPERTY (such as TArray, TSet or TMap), the affected elements will be set as nullptr but not removed

Whenever code references an AActor or a UActorComponent, it has to deal with the possibility that AActor::Destroy could be called on the actor or UActorComponent::DestroyComponent could be called on the component. These functions will mark them for pending kill, thus triggering their garbage collection at the first opportunity (note that destroying an actor also destroys all its components).

Since the garbage collector automatically nulls out UPROPERTY pointers when it actually gets to destroy them, null-checking an actor or component pointer is sufficient to know it's safe to use, though you might also want to check IsPendingKill on them to avoid accessing them after they have been marked for destruction (TWeakObjectPtr already checks for this when retrieving the raw pointer).

bool IsValid(const UObject* Test)

is a global function that automatically checks if an object pointer is non-null and not pending kill.

UObjectBase::IsValidLowLevel
UObjectBase::IsValidLowLevelFast

Should not be used for Garbage Collection checks, as on UPROPERTY pointers it will always return true, while on raw pointer it will return true or false depending whether the object had already been destroyed, but in the latter case it's also likely to also crash the application as the pointed memory could have been overwritten.

If you write your own non-garbage classes that references garbage collected objects, you may want to sub-class FGCObject.

Unreal Smart Pointer Library

Unreal Smart Pointers (TUniquePtr, TSharedPtr, TWeakPtr) is a smart memory management system, for code that is not based on the Unreal Object system, designed to ease the burden of memory allocation and tracking. It also adds TSharedRef, which acts like a non-nullable Shared Pointer. Unreal Smart Pointers cannot be used with Unreal Objects, because they use a separate memory-tracking system that is better-tuned for game code.

Structs vs. Objects

Structures are intended for use as 'value' types. They are best suited for small amounts of data to be reused in objects and actors. For example, FVector, FRotator, FQuat. They are not deleted, so they must always exist within a UObject.

One advantage of UStructs is that they are very small. While a UObject must contain accounting data in addition to your data, UStructs (technically UScriptStructs) are only as large as the data you store in them. The garbage collector doesn't have to do as much work to manage them.

UObjects are collected by the garbage collector. Although they are heavier, it is generally safe to point to them. Keep in mind that each UObject is another thing for the garbage collector to keep track of. Although the engine can easily handle thousands of objects, this capability should be used carefully. If your project requires thousands of instances of something and UStructs are acceptable, they will generally be more powerful than UObjects.

Containers

For the garbage collector to do its work of determining what is safe to delete, it must traverse every field of every object. While Unreal provides several types of containers (TArray, TMap, …) the garbage collector only considers pointers in TArray.

See Also

Some more specifics and code examples related to memory management, see Dynamic Memory Allocation.

FAQ

What is Garbage Collection?
Garbage Collection is a feature of many modern languages that you may have already used (C#, Python, Javascript, etc.) so you may already be using it without even knowing! In a garbage collected environment objects are automatically removed from memory after you stop using them. This means you can create a new object, use it for a while, and when you are done using it you set the variable that points to it as null and that's the end of your worries. Behind the scene the garbage collector ("GC") is keeping track of what objects are still being used. When an object is no longer being used the garbage collector automatically frees up the memory. Lower level languages such as C and C++ do not provide a garbage collector out of the box. This means you have to manually keep track of what memory is being used and free it when you no longer wish to use it. This can be bug prone and is harder for a programmer to manage so Unreal Engine 4 has created their own Garbage Collection system.
How does Garbage Collection work in UE4 (Technical Explanation)
When a UObject-derived object is instantiated it gets registered with Unreal Engine's garbage collection system. The garbage collection system automatically runs every 30-60 seconds (or less depending on how much free memory remains on the system) and looks for any objects which are no longer being used and removes them. The way it does this is the GC system has a "Root Set" of objects that it knows should permanently be alive. The GC system uses reflection (another feature that C++ lacks which UE4 has built ontop of the language as well) to look at the properties of your object and follow references to other objects, and then those objects's properties, etc. If an object is found by traversing through other objects and one of those objects is contained within the root set then the object is considered reachable and kept alive. Once the GC has gone through every object if there is no way to reach an object in the root set by looking at the references the object is considered unreachable and marked for garbage collection. When an object is garbage collected the memory that represents it is free'd and returned to the system. Any pointers that pointed to this object will have their pointers set to null and will cause a crash if you try to use them. If you properly use UE4's decorators you should never come across this issue.
How do I write C++ Code that Uses Unreal's GC System?
The first thing to note is that it's important to understand when you need to be worried about garbage collection. If you have a pointer inside of a function you do not have to worry about the Garbage Collection system. These pointers inside of functions act like normal C/C++ pointers and do not need any changes.
void AMyClass::CheckIfWeHaveOwner()
{
// Get a pointer to our Owner
AActor* MyOwner = GetOwner();
if(IsValid(MyOwner))
{
// Do something.
}
}

However, if you want to have a pointer to an object and have it exist for more than one frame you will need to store it in one of two ways:

  1. The pointer must be stored a member variable in your class and you must add the UPROPERTY(...) macro before it. There are many variations of what can go inside of UPROPERTY() but you do not need any of them to have the reference that follows it be considered by the Garbage Collection system.
UCLASS()
class AMyClass : public AActor
{
GENERATED_BODY()
private:
// UPROPERTY() declares that you want MyReferenceToAnotherActor to be considered by the Garbage Collection system.
UPROPERTY()
AActor* MyReferenceToAnotherActor;
}

Simply by adding the UPROPERTY() macro before the AActor pointer you have informed Unreal Build Tool to automatically generate the code you need for the object to properly work with Unreal's Garbage Collection system. The UPROPERTY() macro can only be used on classes which drive from "UObject". If you need to manage the memory of a non-Unreal C++ class you will need to see the section below on "Manual Memory Management". Easy right!

How do I decide to destroy something manually?
Objects which derive from the *AActor* class (which derives from **UObject** and thus can be part of the GC system) implement a Destroy function. When you Destroy an actor it will remove itself from the world at the end of the frame. This means that it will continue to exist (and pointers will still be valid) until the end of the frame, at which point they will become null when it is removed. You can often come across situations where you need to know **if the pointer to an object is still valid, and that object is not being destroyed**. You can do this with the `IsValid(...)` function, where you pass in a pointer to an object. `IsValid()` will return false if the pointer is `nullptr` or if `Destroy()` has been called on that object and it has not yet been removed from the world.

ToDo: How do you destroy a UObject?

Do I need to do anything to float/int32/etc?
Nope! Classes that derive from `UObject` are the only things that can be considered for garbage collection. While it is outside of the scope of this tutorial, structs and standard data types do not need memory management and thus are not part of the garbage collection system. You may still see `UPROPERTY()` macros on these data types but that is often for exposing them to blueprints and not just for the garbage collection system.
How do I manually manage memory?
If you are working with third party C/C++ apis or when you are doing operations on normal C++ classes created in memory that do not derive from UObject you will need to manage their memory on your own.

Unreal Engine has overrides new and delete in their database to work with their memory management. However, this does not mean it is part of garbage collection and you will need to use new and delete for objects to be properly initialized.

You can create a new instance via the new keyword and you can delete it using delete. It is highly important that you call delete and free up memory otherwise you will create a memory leak. These normal C++ classes should be contained by an Unreal-exposed class (such as an Actor or UObject) and when that Actor/UObject is destroyed you should free up the memory. Remember, it's manually managed which means you are responsible for deleting the memory if you allocate it.

I know what Im doing and the Root Set
Unreal Engine has overrides `new` and `delete` in their database to work with their memory management. However, this **does not mean it is part of garbage collection** and you will need to use `new` and `delete` for objects to be properly initialized.
I know what Im doing and the Root Set
This assumes you actually know what youre doing. Dont `YourObjectInstance->AddToRoot()` something to the root set because youre having crashes or other issues you dont understand. `YourObjectInstance->AddToRoot();