Introduction

Unreal Engine uses an automatic garbage collection system to manage memory and clean up unused objects. However, when GC is triggered too frequently or at inopportune moments, it can cause performance hitches, frame drops, and stuttering. This issue becomes particularly problematic in large, open-world games or projects with high object churn. This article explores common causes of garbage collection performance issues in Unreal Engine, debugging techniques, and best practices for optimizing memory management.

Common Causes of Garbage Collection Performance Issues

1. Excessive Garbage Collection Calls During Gameplay

Unreal Engine automatically triggers garbage collection based on allocated memory, but frequent GC execution can cause performance drops.

Problematic Scenario

// Default GC threshold
[/Script/Engine.GarbageCollectionSettings]
timeBetweenPurgingPendingKillObjects=5.0

By default, Unreal Engine purges unused objects every 5 seconds, which can lead to noticeable stuttering.

Solution: Increase GC Threshold to Reduce Frequency

// Increase threshold to reduce GC interruptions
[/Script/Engine.GarbageCollectionSettings]
timeBetweenPurgingPendingKillObjects=30.0

Increasing `timeBetweenPurgingPendingKillObjects` reduces the frequency of garbage collection cycles, preventing frequent performance drops.

2. Large Numbers of Unreferenced Objects Causing Memory Spikes

Unreal Engine’s GC only collects unreferenced objects. If objects are not properly dereferenced, they accumulate in memory.

Problematic Scenario

UPROPERTY()
UTexture2D* UnusedTexture;

If `UnusedTexture` is never set to `nullptr`, it remains in memory indefinitely.

Solution: Explicitly Dereference Objects Before They Become Unused

UnusedTexture = nullptr;
MarkPendingKill();

Setting objects to `nullptr` ensures they are properly flagged for garbage collection.

3. Excessive Use of `Tick` Functions Leading to Unnecessary Object Retention

Using `Tick()` for continuous updates on multiple objects can cause performance issues if not managed correctly.

Problematic Scenario

void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    ProcessObject();
}

If `ProcessObject()` creates new objects each frame without proper cleanup, the engine will accumulate unused objects in memory.

Solution: Disable Tick When Not Needed

void AMyActor::BeginPlay()
{
    Super::BeginPlay();
    SetActorTickEnabled(false);
}

Disabling `Tick()` when not required prevents unnecessary object creation and retention.

4. Improperly Managed Dynamic Object Spawning

Creating objects dynamically without proper destruction leads to excessive memory usage.

Problematic Scenario

UWorld* World = GetWorld();
AActor* NewActor = World->SpawnActor(MyActorClass, SpawnLocation, SpawnRotation);

If `NewActor` is not properly destroyed, memory usage will increase over time.

Solution: Use Smart Pointers and Proper Cleanup

if (NewActor)
{
    NewActor->Destroy();
}

Destroying unused actors prevents memory leaks and excessive GC load.

5. Inefficient Use of UPROPERTY Markup Leading to Uncollected Objects

Using incorrect `UPROPERTY` specifiers can prevent objects from being garbage collected.

Problematic Scenario

UPROPERTY()
UObject* MyObject;

If `MyObject` is marked with `UPROPERTY()` but is not intended to persist, it may not be collected.

Solution: Use `Transient` or `WeakPtr` Where Appropriate

UPROPERTY(Transient)
UObject* MyObject;

Marking objects as `Transient` ensures they are not retained longer than necessary.

Best Practices for Optimizing Garbage Collection in Unreal Engine

1. Adjust GC Settings to Reduce Stuttering

Increase `timeBetweenPurgingPendingKillObjects` to avoid frequent GC cycles.

Example:

[/Script/Engine.GarbageCollectionSettings]
timeBetweenPurgingPendingKillObjects=30.0

2. Explicitly Dereference Objects When No Longer Needed

Set unused objects to `nullptr` to ensure proper GC handling.

Example:

MyObject = nullptr;

3. Disable `Tick()` When Not Required

Prevent unnecessary updates by disabling `Tick()` in idle actors.

Example:

SetActorTickEnabled(false);

4. Use Smart Pointers for Dynamic Object Management

Use `TWeakObjectPtr` instead of raw pointers for temporary objects.

Example:

TWeakObjectPtr TempObject;

5. Monitor GC Performance Using Unreal Insights

Use Unreal Insights to track memory usage and GC impact.

Example:

Window → Developer Tools → Unreal Insights

Conclusion

Performance degradation and unexpected stuttering in Unreal Engine often result from improper garbage collection management, excessive object retention, and inefficient memory usage. By adjusting GC settings, explicitly dereferencing objects, disabling unnecessary `Tick()` functions, and using Unreal Insights for performance monitoring, developers can optimize memory management and maintain a smooth gameplay experience.