LoginSignup
10
5

More than 3 years have passed since last update.

[Unity]Incremental GC を試してみた

Last updated at Posted at 2019-07-31

歴史

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) にチェックを入れます。
image.png

実験

早速挙動を確認してみます。なお、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 の状態で試してみます。

image.png

ちゃんとオフになっています。ここで Profiler を見ると

image.png

毎フレーム GC 走ってるやん!
なんで!?

  終
制作・著作
━━━━━
ⓊⓃⒾⓉⓎ

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5