🌐 Website: www.gameoptim.com
We recently ran into a strange Mono memory issue while profiling a production project.
In the GameOptim GOT Online Mono Mode report, Used Mono memory kept increasing continuously over time.
At first glance, it looked exactly like a classic memory leak.
But there was one detail that didn’t fit:
The total amount of Managed Objects remained almost unchanged.
That raised an important question:
If live objects aren’t increasing, why is Mono memory still growing?
After deeper analysis, the root cause turned out not to be a memory leak at all — but heap fragmentation.
When Mono Memory Grows, It Doesn’t Always Mean Object Growth
A common misunderstanding in Unity memory analysis is assuming:
Mono memory up = more objects alive
But in many cases, that’s not true.
In the GOT Online Mono Mode report:
Used Mono = Managed Objects + Heap Fragmentation
These two parts are displayed separately in the Heap Object Snapshot:
- Pink = actual live Managed Objects
- Blue = fragmented heap space
Because increasing Mono memory can come from either side.
The Suspicious Pattern
In this case, we observed that between frame 15000 and 20000, Used Mono kept rising steadily.
The usual troubleshooting process was straightforward:
- Inspect resident objects
- Look for object retention
- Verify whether allocations are accumulating
At first, this strongly suggested a memory leak.
But after checking the resident object snapshots, the object count remained relatively stable.
There was no growth pattern matching the Mono increase.
That was the first sign that the issue might not be object retention.
Breaking Down the Heap Composition
Once we separated Used Mono into its components, the pattern became much clearer.
The increase was not coming from Managed Objects.
It was coming almost entirely from heap fragmentation.
If:
- Managed Object count remains stable
- Used Mono keeps increasing
- Fragmentation ratio keeps expanding
Then the issue is very likely heap fragmentation, not a memory leak.
This distinction matters because the optimization direction is completely different.
Why Mono Heap Fragmentation Happens
This behavior comes from how the Mono heap works internally.
Mono usually expands on demand.
But once heap space has been allocated, it typically does not immediately return memory back to the operating system after objects are released.
This creates an important side effect.
If a project repeatedly performs:
Allocate → Release → Allocate → Release
especially with small temporary objects,
it can leave behind many small free regions inside the heap.
These regions may be reusable individually, but often cannot be merged into large continuous blocks.
Over time:
- fragmentation increases
- allocation efficiency drops
- heap expansion continues
Even if actual live objects remain stable.
This is one of the most typical fragmentation patterns in Mono-based Unity projects.
How Severe Can It Get?
In some projects, fragmentation can become almost equal to actual object memory.
For example:
- Actual Managed Objects: ~200 MB
- Final Used Mono: ~400 MB
That means half of the Mono heap is effectively unusable fragmented space.
At that point, the problem is no longer:
Who forgot to release memory?
But:
Who is allocating too frequently?
That’s a very different investigation.
Common Sources of Heap Fragmentation
In production Unity projects, common sources include:
1. LINQ allocations
Frequent use of:
.Where().Select().ToList()
can create many temporary enumerators and collections.
2. String concatenation
Especially inside Update loops:
text = "HP: " + currentHP;
This generates temporary strings constantly.
3. Boxing / Unboxing
Value types converted to object:
object obj = intValue;
can silently allocate.
4. Coroutine closures
Captured variables inside coroutines may allocate hidden objects.
5. Temporary Lists
Repeated:
new List<T>()
inside hot paths can become fragmentation hotspots.
6. Frequent delegate creation
Lambda expressions and temporary delegates can generate short-lived allocations.
Quick Diagnosis Checklist
When Mono memory keeps growing, check these first:
✔ Is Managed Object count growing?
✔ Is fragmentation ratio growing faster than objects?
✔ Do heap snapshots show stable live objects?
✔ Are temporary allocations happening in hot loops?
If the answer matches this pattern, you’re likely dealing with fragmentation.
Not a leak.
Final Thoughts
When analyzing Mono memory issues, the first instinct is often to search for unreleased objects.
That’s reasonable.
But not every memory increase means object retention.
Sometimes the real issue is simply allocation frequency.
Before deciding how to optimize:
Always break down Used Mono into:
- actual object growth
- fragmentation growth
That distinction determines whether you should optimize:
object lifetime
or
allocation behavior
And in performance optimization, that difference can save a lot of time.


Top comments (0)