Overview
When developing Unity games, you may encounter stuttering (spikes) after long play sessions or during specific scenes. The culprit is often Garbage Collection (GC), a memory management mechanism.
In managed languages like C#, programmers don't need to manually free memory—the garbage collector automatically finds and cleans up unused memory. While convenient, this "cleanup process" temporarily pauses program execution. That's what causes the stuttering.
This article explains why garbage collection causes performance issues, what code patterns generate unnecessary memory (garbage), and professional coding techniques to minimize GC, based on Unity official documentation and insights from top developers.
Why Is GC the Enemy?
C# memory divides into two main areas: Stack and Heap.
- Stack: Stores value types (
int,float,bool) and local function variables. Management is extremely fast—memory is automatically freed when functions return. - Heap: Stores
classinstances (objects), strings, arrays, and other reference types. Can hold larger data for longer periods, but management is complex.
The heap is where problems arise. When your program creates new objects with new, memory is allocated on the heap. When objects become unused, they become "garbage." When the heap fills up, the garbage collector activates to clean up and reclaim space. During this cleanup, game execution stops completely (Stop-The-World).
Common Garbage Sources and Solutions
The most effective way to prevent GC is to reduce heap allocations—write code that minimizes garbage generation.
1. String Concatenation
Bad Example: The string + operator creates new string objects internally each time, generating lots of garbage.
void Update()
{
// Creates new string on heap every frame
string message = "Player Health: " + currentHealth;
uiText.text = message;
}
Solution: Use the StringBuilder class. It has an internal buffer and doesn't create new objects when appending.
private StringBuilder sb = new StringBuilder();
void Update()
{
// No garbage generated
sb.Clear();
sb.Append("Player Health: ");
sb.Append(currentHealth);
uiText.text = sb.ToString(); // Allocation only on final ToString()
}
2. Excessive new Keywords
Bad Example: Creating arrays or class instances with new in Update generates garbage every frame.
void Update()
{
// Creates new array every frame
Vector3[] positions = new Vector3[10];
// ...
}
Solution: Use Object Pooling. For frequently spawned/destroyed objects like bullets, enemies, and effects, pre-create a set quantity, keep them inactive, and reuse them when needed. This dramatically reduces runtime Instantiate and Destroy calls (both cause heap allocations internally).
3. yield return new WaitForSeconds() in Coroutines
Bad Example: Using new WaitForSeconds() in coroutine loops creates new objects each iteration.
IEnumerator FadeOut()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
// Creates new object each time
yield return new WaitForSeconds(0.1f);
}
}
Solution: Create the WaitForSeconds object once and cache it for reuse.
private WaitForSeconds delay = new WaitForSeconds(0.1f);
IEnumerator FadeOut()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
// Reuse cached instance
yield return delay;
}
}
4. foreach Loops
In older Unity versions (pre-2020.1), foreach loops could generate garbage internally. This is mostly fixed in recent versions, but for legacy projects or maximum performance, traditional for loops are safer.
5. LINQ and Lambda Expressions
LINQ methods (Where, Select, etc.) make code concise but can cause significant heap allocations internally. Avoid heavy use in performance-critical sections like Update.
Identifying GC Allocations with the Profiler
To identify where garbage is generated, use Unity's Profiler (Window > Analysis > Profiler).
- Open the Profiler and select the target platform (Editor or connected device).
- Select the
CPU Usagemodule and pressRecord, then play the game. - Click frames showing spikes in the graph.
- In the detailed view below, focus on the
GC Alloccolumn. Functions showing non-zero values are causing heap allocations (garbage sources). - Click function names to drill down and identify specific causes.
Summary
Garbage collection is an eternal challenge in Unity development. On resource-limited mobile platforms especially, GC management directly affects game quality.
- GC pauses game execution while cleaning heap memory, causing stuttering.
- To avoid GC, reduce heap allocations (garbage generation).
- Avoid string concatenation in
Update, excessivenew, and uncached coroutine yields. - Object pooling is an extremely effective technique.
- Use the Profiler to constantly monitor where garbage is generated.
Making "garbage-conscious coding" habitual enables development of smooth, professional games free from user-experience-degrading stutters.