Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unityにおけるメモリ管理

Cygames
November 30, 2015

 Unityにおけるメモリ管理

2015/11/15 Cygames Tech Fes

Cygames

November 30, 2015
Tweet

More Decks by Cygames

Other Decks in Programming

Transcript

  1. 自己紹介
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • 高原 光示
    • Cygamesでエンジニアリーダーをやっています
    • 以前はPC向けのオンラインゲームを作ってい
    ました
    • 3D・リアルタイム通信・セキュリティが得意

    View full-size slide

  2. 今日のおはなし
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • Unityのメモリについておさらい
    • メモリリーク事例の紹介
    • メモリリークの探し方
    • まとめ

    View full-size slide

  3. なぜメモリの管理が必要か?
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • メモリを無駄に使うとアプリのクラッシュに繋が

    • クラッシュしない場合でも、アプリをバックグラ
    ウンドにした際にOSからタスクキルされやすく
    なる

    View full-size slide

  4. Unityのメモリの種類
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • Mono管理
    • Unity管理

    View full-size slide

  5. Mono管理のメモリ
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • Stack
    – Intやfloatなどの値型
    – Structやenum
    • Heap
    – Classやdelegateなどの参照型
    – 参照されなくなったインスタンスはGC時で解放
    – メモリが確保できなくなると自動的にメモリ領域を
    拡張する
    • ピーク使用量を抑える必要がある
    • ファイルIOなどで無駄に確保しすぎると危険

    View full-size slide

  6. 参照されないとは?
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • インスタンスを変数に格納すると「参照」してる
    ことになる
    • Unityの場合、シーン上のオブジェクトか静的
    フィールドから変数をたどっていくことができる
    場合「参照」されている

    View full-size slide

  7. Unity管理のメモリ
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • TextureやMesh等のアセット類
    • Resources.Load
    • AssetBundle.LoadAsset
    • Resources.UnloadAsset
    • Resources.UnloadUnusedAssets

    View full-size slide

  8. Resources.UnloadUnusedAssets
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • アプリから参照されていないリソースを破棄
    • シーン遷移時にも同等の処理が行われる
    • 個別のリソースをUnloadするのは大変面倒な
    ので、
    基本的にはこの関数を使ってリソースを管理す
    ることになる

    View full-size slide

  9. メモリリーク事例の紹介
    (Unity5.2.1)
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  10. リソースが解放されない例1 (1/3)
    public class AssetLoader : MonoBehaviour
    {
    Object reference;
    void Awake()
    {
    // リソースをLoadして参照を保持するだけ
    reference = Resources.Load("texture");
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  11. リソースが解放されない例1 (2/3)
    public class Sample1 : MonoBehaviour
    {
    public AssetLoader obj;
    IEnumerator Start()
    {
    Destroy(obj);
    // 実際にDestroyされるのはフレームの最後なので1フレーム待つ
    yield return 0;
    Debug.Log(obj); // => null
    Resources.UnloadUnusedAssets(); // アセットは解放されない!
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  12. リソースが解放されない例1 (3/3)
    public class Sample1 : MonoBehaviour
    {
    public AssetLoader obj;
    IEnumerator Start()
    {
    Destroy(obj);
    // 実際にDestroyされるのはフレームの最後なので1フレーム待つ
    yield return 0;
    Debug.Log(obj); // => null
    obj = null;
    Resources.UnloadUnusedAssets(); // OK.
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  13. リソースが解放されない例2 (1/4)
    public class AssetLoader : MonoBehaviour
    {
    Object reference;
    void Awake()
    {
    reference = Resources.Load("texture");
    }
    public void Log()
    {
    Debug.Log("hoge");
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  14. リソースが解放されない例2 (2/4)
    public class Sample2 : MonoBehaviour
    {
    public AssetLoader obj;
    public System.Action action { get; set; }
    void Start()
    {
    action = obj.Log;
    Destroy(obj);
    obj = null;
    Resources.UnloadUnusedAssets(); // アセットは解放されない!
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  15. リソースが解放されない例2 (3/4)
    public class Sample2 : MonoBehaviour
    {
    public AssetLoader obj;
    public System.Action action { get; set; }
    void Start()
    {
    action = obj.Log;
    Destroy(obj);
    obj = null;
    action = null;
    Resources.UnloadUnusedAssets(); // OK.
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  16. リソースが解放されない例2 (4/4)
    public class Sample2 : MonoBehaviour
    {
    public AssetLoader obj;
    public System.Action action { get; set; }
    void Start()
    {
    // lambdaを渡すと参照されていないことになる
    action = () => obj.Log();
    Destroy(obj);
    obj = null;
    Resources.UnloadUnusedAssets(); // OK.
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  17. リソースが解放されない例3 (1/3)
    public class SingletonMonoBehaviour : MonoBehaviour where T : MonoBehaviour
    {
    private static T instance = null;
    public static T Instance
    {
    get
    {
    if (instance == null)
    {
    instance = FindObjectOfType();
    }
    return instance;
    }
    }
    void Awake()
    {
    if (instance != null && instance != this)
    {
    Destroy(gameObject);
    return;
    }
    instance = this as T;
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  18. リソースが解放されない例3 (2/3)
    public class Scene1 : SingletonMonoBehaviour
    {
    Object reference;
    void Start()
    {
    reference = Resources.Load("texture");
    // DontDestroyOnLoadを指定していないので、
    // リソースは破棄されるはず・・・
    Application.LoadLevel("Scene2");
    // シーン遷移後にリソースが破棄されていない!
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  19. リソースが解放されない例3 (3/3)
    public class SingletonMonoBehaviour : MonoBehaviour where T :
    MonoBehaviour
    {
    // 省略
    // Destroyされる前に参照を外す必要がある
    void OnDestroy()
    {
    if (instance == this)
    instance = null;
    }
    }
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  20. メモリリークの探し方
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  21. 2.Detailedを選択
    3.TakeSampleEditorを押す押
    したフレームのメモリダンプが
    表示される
    1.Memoryを選択
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  22. メモリ上のリソースが意図したも
    のかどうか確認
    参照してるObjectをヒントに
    怪しいコードを探す
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide

  23. TIPS
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • Inspector等でアセットを表示するとリソースが
    解放されない
    – 調査時に混乱するので、Hierarchyの何もない部
    分をクリックしておく
    • ReferencedByに
    ManagedStaticReferences()と表示されてる
    のはstaticなインスタンスからの参照
    – 全てのstatic変数を疑わないといけない

    View full-size slide

  24. まとめ
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE
    • リソースの管理はシンプルに
    • 静的変数とdelegateはリークに繋がる事が多
    いので要注意
    • メモリのダンプをとれるAPIが欲しい
    • むしろUnityがDestroyされたオブジェクトから
    は参照されてないことにすれば大体解決する
    のでは・・・!

    View full-size slide

  25. ご静聴ありがとうございました
    $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE

    View full-size slide