インクリメンタル ガベージコレクション (GC) は、ガベージコレクションの処理を複数のフレームに分散して行います。これは、Unity のデフォルトのガベージコレクションの動作です。
Unity は、Boehm-Demers-Weiser ガベージコレクター を使用しています。デフォルトで、Unity はインクリメンタル (増分) モードで使用します。つまり、ガベージコレクターは、メインの CPU スレッドを停止する代わりに (stop-the-world garbage collection) 複数のフレームにわたって作業量を分割して、マネージヒープのすべてのオブジェクトを処理します。これは、ガベージコレクターがマネージヒープのオブジェクトを処理するための中断をに 1 回に長く行うのではなく、実行中のアプリケーションの中断をより短くすることを意味します。
インクリメンタルモードでは、ガベージコレクションが全体的に速くなるわけではありませんが、ワークロードを複数のフレームに分散させるため、GC 関連のパフォーマンススパイクが減少します。このような中断は GC スパイク と呼ばれます。これは、Profiler ウィンドウ のフレームタイムグラフに大きなスパイクとして表示されるためです。
インクリメンタルモード (Edit > Project Settings > Player > Other Settings > Configuration > Use Incremental GC) を無効にする場合、ガベージコレクターはコレクションパスを実行する際にヒープ全体を調べる必要があります。これは、stop-the-world ガベージコレクションとして知られています。なぜなら、ガベージコレク ターが実行されるたびに、メインの CPU スレッドを停止するからです。マネージヒープのすべてのオブジェクトを処理した後にのみ実行が再開されるため、アプリケーションのパフォーマンスに影響を与える GC スパイクを発生させる可能性があります。また、ガベージコレクターは非圧縮型です。つまり、Unity はオブジェクト間のギャップを埋めるためにメモリ内のオブジェクトを再分配しません。
Important: The Web platform doesn’t support incremental garbage collection.
インクリメンタルガベージコレクションが無効になっている場合、ガベージコレクションを実行するためにUnity がプログラムコードの実行を停止すると、GC スパイクが発生します。この遅延は、ガベージコレクターが処理する必要のある割り当ての数や、アプリケーションが実行されているプラットフォームに応じて、数百ミリ秒続くことがあります。
これは、ゲームなどのリアルタイムアプリケーションでは問題となります。ガベージコレクターがアプリケーションの実行を中断すると、スムーズなアニメーションに必要な一定のフレームレートを維持することが難しくなるからです。
以下のプロファイラーのスクリーンショットは、インクリメンタルガベージコレクションがフレームレートの問題を軽減することを示しています。
上のプロファイリングセッションでは、インクリメンタル GC が有効になっています。これは、ガベージコレクターがガベージコレクション操作を複数のフレームに分散させ、各フレームの小さなタイムスライスを使用しているためです (黄色の VSync のすぐ上にある暗い緑色の部分)。
下のプロファイリングセッションでは、インクリメンタル GC を無効にしていますが、明らかに GC スパイクが見られます。このスパイクにより 60FPS の滑らかなフレームレートが中断され、ガベージコレクションが発生するフレームが、60FPS を維持するために必要な 16MS の制限を超えています。
アプリケーションが VSync または Application.targetFrameRate を使用する場合、Unity はガベージコレクションに割り当てる時間を、利用可能な残りのフレーム時間に基づいて調整します。このようにすることで、Unityは、本来ならば待機する時間にガベージコレクションを実行することができ、パフォーマンスへの影響を最小限に抑えることができます。
ノート: VSync Count を Don’t Sync 以外に設定する (プロジェクトの Quality settings または Application.VSync プロパティで) 場合、または Application.targetFrameRate プロパティを有効にする場合、Unity は自動的にフレームの終わりに残っている何にも使用されていない時間をインクリメンタル GC に使用します。
インクリメンタル GC の動作をより正確に制御するには、Scripting.GarbageCollector クラスを使用します。例えば、VSync やターゲットフレームレートを使用したくない場合、フレーム終了までの時間を自身で計算し、その時間をガベージコレクターに入力して使用することができます。
インクリメンタルガベージコレクションは、アプリケーションにとって問題となる場合があります。なぜなら、ガベージコレクターがこのモードで作業を分割する際に、マーキングフェーズも分割するからです。マーキングフェーズとは、ガベージコレクターがすべてのマネージオブジェクトをスキャンし、どのオブジェクトがまだ使用されているか、どのオブジェクトを消去できるかを判断するフェーズです。
マーキングフェーズの分割は、オブジェクト間の参照のほとんどが作業スライスの間で変更されない場合にはうまくいきます。しかし、オブジェクトの参照が変更されると、ガベージコレクターは次のイテレーションでそれらのオブジェクトを再度スキャンしなければなりません。つまり、あまりにも多くの変更が行われるとインクリメンタルガベージコレクターに負荷を与える可能性があり、常に多くの仕事があるため、マーキングフェーズが決して終了しない状況を作り出すことを意味します。このような状況が発生すると、ガベージコレクターは、完全な、非インクリメンタルコレクションを行うようにフォールバックします。
Unity がインクリメンタルガベージコレクションを使用する場合、追加コード (ライトバリアと呼ばれる) を生成し、参照が変更されるとガベージコレクターに通知します。これにより、オブジェクトを再スキャンする必要があるかどうか確認できます。これにより、参照を変更する 際にいくらかのオーバーヘッドを追加し、これはマネージコードのパフォーマンスに影響します。
インクリメンタルガベージコレクションを無効にするには、 Player Settings ウィンドウ (Edit > Project Settings > Player > Configuration) を開き、 Use incremental GC を無効にします。ほとんどの Unity プロジェクトで、特にガベージコレクションのスパイクに悩まされている場合は、インクリメンタルガベージコレクションによる恩恵があります。しかし、常に プロファイラー を使用して、アプリケーションが期待通りに動作するかを確認する必要があります。