How I broke Incremental GC in Unity

Once upon a time, before releasing Summer Catchers on Steam, I was trying to make our game as smooth as possible. Since we are using object pooling our game almost does not have memory allocations after a new scene is loaded and thus it does not require to use GC.

So I thought it would be nice to forbid GC to do its job while the scene is being played. Luckily Unity provides an API for that: GarbageCollector.GCMode. Thereby we used the code similar to the following one (very simplified though)

And it actually worked. I don’t know how effective it was, I mean was it really necessary and wasn’t that problem a phony one. Anyway, it did not break anything, GC was doing its job when one scene was unloaded before another one was loaded. We shipped the game on PC, Mac, iOS, tvOS, Android and never experienced any problems with memory or whatsoever.

But then something changed. We were porting the game on Nintendo Switch and during testing we encountered a very weird and actually terrifying behaviour: in some cases when a new scene started the performance dropped so hard that the game transformed into a slideshow. Later this happened on iPad too.

I had no clues regarding what could caused such an awful drop of performance. So I turned on the profiler and…

GarbageCollector turned into TimeCollector

Whoa! GarbageCollector? GarbageCollector.CollectIncremental?!
Here I must say that some time before all this happened we updated to the latest (by that time) version of Unity 2019 which received a new GC with incremental mode. The purpose of incremental mode is to make GC do its job not all at once during one frame, but to slice the job over several frames while trying to keep the per-frame time at some given constant time.
But in our case something went wrong and GC started to do its job longer and longer each new frame ignoring the time threshold. It all started at harmless microseconds and grew up to unimaginable seconds! It looked like an ant mill (or death spiral): each new frame was taking more time than the previous one leading to inevitable unplayable disaster.

I actually have no idea why disabling GC at start of the scene caused this behaviour. And also why it happened only in some rare cases. But removing that GC-disabling trick stopped it.

Unfortunately it’s really hard to create a minimal working example with such behaviour to make a bugreport. So in this post I just wanted to share my experience with a hope that it may help anyone who’ll be looking at dangerously increasing time value next to the “GarbageCollector.CollectIncremental” label.

Share Comments
comments powered by Disqus