歴史
2018.3.0 以前
GCとはガベージコレクション(Garbage Collection)の略で、不要になったメモリ領域を確保する機能です。
従来のUnityではGCの際にかなり大きなスパイクが発生してしまうことが問題となっていました。
例えばフレーム単位の操作精度を要求されるアクションゲームなどではスパイクの発生が絶対に許されません。
そのため、ヒープメモリをできる限り使わないようにとても気を使いながらプログラミングする必要がありました。
たとえば
- ラムダ式を使わない
- LINQを使わない
- とことんnewを避ける(プールして使い回す)
- ヒープメモリを確保する標準APIを呼ばない(Input.touchesの代わりにInput.GetTouchを使う)
- 文字列を連結しない
- foreachを使わない(Unity 5.5以前のバグによりヒープメモリを確保してしまう)
など…
2018.3.0
2018.3.0 において GarbageCollector なる API が登場しました。
GarbageCollector.GCMode = GarbageCollector.Mode.Disabled;
とすることにより、GCを強制的に発生させないようにすることができるようになりました。
とはいえ、GCを止めている間は当然メモリ使用量が減らないため、
システムメモリ不足によってアプリケーションが強制終了する可能性があります。
そのため使用できる場面は限定的でした。
Incremental GC
概要
2019.1.0 において Incremental GC が試験的に導入されました。
従来のGCが1フレーム内で処理されていたのに対して
Incremental GC では処理が複数フレームに渡ってちょっとずつ実行されます。
そのため大きなスパイクは発生しないというわけですね。
有効にするには Player Settings の Use incremental GC (Experimental) にチェックを入れます。
実験
早速挙動を確認してみます。なお、Editorでは動かないのでビルドして試しましょう。
UnityEngine.Scripting.GarbageCollector に何やらAPIが追加されています。
API | 内容 |
---|---|
isIncremental | Incremental GC が有効か(read only) |
incrementalTimeSliceNanoseconds | 1フレームあたりにGCに割く時間(ナノ秒) |
CollectIncremental | Incremental GCを手動実行する |
今回はこんな感じのスクリプトを用意します。
using System.Linq;
using UnityEngine;
using UnityEngine.Scripting;
using UnityEngine.UI;
public class Test : MonoBehaviour
{
[SerializeField] private Text _text;
[SerializeField] private Slider _slider;
void Start()
{
void UpdateText() => _text.text = string.Format(
"Enabled = {0}\nTime = {1}[ms]",
GarbageCollector.isIncremental,
GarbageCollector.incrementalTimeSliceNanoseconds / 1000 / 1000
);
// スライダーでミリ秒を設定する
_slider.onValueChanged.AddListener(value =>
{
GarbageCollector.incrementalTimeSliceNanoseconds = (ulong)value * 1000 * 1000;
UpdateText();
});
UpdateText();
}
void Update()
{
// 適当にヒープメモリを確保する
new int[100000].Select(i => i.ToString()).ToArray();
}
}
まずは Incremental GC = OFF の状態で試してみます。
ちゃんとオフになっています。ここで Profiler を見ると
毎フレーム GC 走ってるやん!
なんで!?
終
制作・著作
━━━━━
ⓊⓃⒾⓉⓎ