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

Unityにおけるメモリ管理

510ec964f5d26c2724c883fd7b671e3d?s=47 Cygames
November 30, 2015

 Unityにおけるメモリ管理

2015/11/15 Cygames Tech Fes

510ec964f5d26c2724c883fd7b671e3d?s=128

Cygames

November 30, 2015
Tweet

More Decks by Cygames

Other Decks in Programming

Transcript

  1. None
  2. 自己紹介 $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • 高原 光示

    • Cygamesでエンジニアリーダーをやっています • 以前はPC向けのオンラインゲームを作ってい ました • 3D・リアルタイム通信・セキュリティが得意   
  3. 今日のおはなし $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • Unityのメモリについておさらい •

    メモリリーク事例の紹介 • メモリリークの探し方 • まとめ   
  4. なぜメモリの管理が必要か? $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • メモリを無駄に使うとアプリのクラッシュに繋が る

    • クラッシュしない場合でも、アプリをバックグラ ウンドにした際にOSからタスクキルされやすく なる   
  5. Unityのメモリの種類 $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • Mono管理 •

    Unity管理   
  6. Mono管理のメモリ $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • Stack –

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

    • Unityの場合、シーン上のオブジェクトか静的 フィールドから変数をたどっていくことができる 場合「参照」されている   
  8. Unity管理のメモリ $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • TextureやMesh等のアセット類 •

    Resources.Load • AssetBundle.LoadAsset • Resources.UnloadAsset • Resources.UnloadUnusedAssets   
  9. Resources.UnloadUnusedAssets $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • アプリから参照されていないリソースを破棄 •

    シーン遷移時にも同等の処理が行われる • 個別のリソースをUnloadするのは大変面倒な ので、 基本的にはこの関数を使ってリソースを管理す ることになる   
  10. メモリリーク事例の紹介 (Unity5.2.1) $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE  

    
  11. リソースが解放されない例1 (1/3) public class AssetLoader : MonoBehaviour { Object reference;

    void Awake() { // リソースをLoadして参照を保持するだけ reference = Resources.Load("texture"); } } $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE   
  12. リソースが解放されない例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   
  13. リソースが解放されない例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   
  14. リソースが解放されない例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   
  15. リソースが解放されない例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   
  16. リソースが解放されない例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   
  17. リソースが解放されない例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   
  18. リソースが解放されない例3 (1/3) public class SingletonMonoBehaviour <T> : MonoBehaviour where T

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

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

    MonoBehaviour { // 省略 // Destroyされる前に参照を外す必要がある void OnDestroy() { if (instance == this) instance = null; } } $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE   
  21. メモリリークの探し方 $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE   

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

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

      
  24. TIPS $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • Inspector等でアセットを表示するとリソースが 解放されない

    – 調査時に混乱するので、Hierarchyの何もない部 分をクリックしておく • ReferencedByに ManagedStaticReferences()と表示されてる のはstaticなインスタンスからの参照 – 全てのstatic変数を疑わないといけない   
  25. まとめ $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT  *OD "MM3JHIUT3FTFSWFE • リソースの管理はシンプルに •

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