Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

なぜメモリの管理が必要か? $POGJEFOUJBM $PQZSJHIU ˜$ZHBNFT *OD "MM3JHIUT3FTFSWFE • メモリを無駄に使うとアプリのクラッシュに繋が る • クラッシュしない場合でも、アプリをバックグラ ウンドにした際にOSからタスクキルされやすく なる

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

リソースが解放されない例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

Slide 13

Slide 13 text

リソースが解放されない例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

Slide 14

Slide 14 text

リソースが解放されない例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

Slide 15

Slide 15 text

リソースが解放されない例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

Slide 16

Slide 16 text

リソースが解放されない例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

Slide 17

Slide 17 text

リソースが解放されない例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

Slide 18

Slide 18 text

リソースが解放されない例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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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