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

Gotanda.unity #10 メモリと闘う者達 〜GC編〜

855f5e6f9d42b55bfd2f209bd5c5cdf8?s=47 Nozomi Tanaka
January 23, 2019
2.4k

Gotanda.unity #10 メモリと闘う者達 〜GC編〜

21Pに誤記
誤:クラス型にする? それとも参照型?
正:クラス型にする? それとも値型?

誤記、どっちも同じ・・・

855f5e6f9d42b55bfd2f209bd5c5cdf8?s=128

Nozomi Tanaka

January 23, 2019
Tweet

Transcript

  1. メモリと闘う者達 〜GC編〜 Nozomi Tanaka / @nozomin770 Gotanda.unity #10 2019/1/23

  2. Tanaka Nozomi(タナカ ノゾミ) ・ワンダープラネット株式会社 - グローバルスタジオ所属 ・Unity使い4年半、Cocos2d-xもはじめました ・Ruby、PHPなどを使ったサーバーエンジニアでもあります ・プロジェクトマネジメントとインフラの修行もはじめました ・Twitter:@nozomin770

  3. アジェンダ ・1分でわかるメモリの仕組み ・C#とGC ・GCで死ぬ(フリーズする)ゲームを救えないのか ・ボックス化、ラムダ式、クラス型と構造体、愉快な仲間たち ・まとめ

  4. これは、GCと戦う 熱き勇者達の物語である

  5. 1分で分かるメモリ管理 メモリには2種類の保存場所があります 「スタック」という場所と 「ヒープ」という場所の2つです

  6. スタックのルール 先入れ、先出し、隙間なし 隙間なく積み上げるため効率がいい

  7. ヒープのルール 空いているところにうまいこと入れる 隙間あり、効率はよくない

  8. 全部スタックだと効率がよいのでは? ・スタックが使えるときは限られている ・プリミティブ型はスタックを使えると思えばOK (object、stringは除く) ・その他のほとんどの場合はヒープのメモリを使うことになる

  9. ヒープが悪い? そういうわけじゃない

  10. C#のメモリとの付き合い方 ・C#はガベージコレクション(以下GC)という仕組みで メモリの片付けを自動管理してくれている ・GCが走るとき、UnityではGC終了まで処理が停止してしまう (俗に言う処理落ち)

  11. GCの発生条件をつかもう ・OSの物理メモリが足りない場合 ・利用可能なヒープメモリの使用量が一定のしきい値を超えた場合  なおしきい値は常に変動する(!?) ・GC.Collectメソッドが呼び出された場合

  12. GCが走る世界線を 回避できないのか・・・?

  13. GCの動作原理を支配する ・C#のGCは「到達可能性」を見て回収可能な変数を見ます ・「到達可能性」の変動条件 →ヒープ領域へのメモリ確保(アロケート)が原因 →これは変数宣言時に行われる

  14. 到達可能性の変動を抑える ・ヒープ領域への確保が行われる条件 - ボックス化(ボクシング) - クラス型のオブジェクト生成 ・上記を回避するようにコードを書くことでGCが走らない世界線にたどり 着けるはずだ・・・

  15. どうやって・・・

  16. ボックス化とは ・値型をobject型(参照型)として扱うとき、スタック上からヒープ上に値を コピーする処理を「ボックス化」(boxing)と呼びます ・値型を使いながらもヒープ領域を使ってしまうため、(個々は僅かだが) 無駄に重くなる ・ヒープ領域へのアロケートが行われてしまう(GCを誘発する可能性が 生まれる)

  17. ボックス化を(適度に)避けたい - メソッド引数がobject型になってないか気をつける - Equalsは生で使うとobject引数が多い… - 比較対象クラスへのIEquatable<T>インターフェース実装がお すすめ - 一部のUnityのクラスはDictionaryでボックス化が起きる

    - structでないクラス - またはIEquatable<T>を実装していないクラス - UnityEngine.Objectを継承してるクラスに多い
  18. とはいってもやりすぎは辛い ・ボックス化が起きる条件は値型の取り扱いのミスに多い ・int型をobject型に変換 ・ジェネリックは使ってOK ・(List<T>にintを入れてもboxingされない) ・GCが起きていい時はボックス化に気にする必要はない (と思ってます・・・)

  19. クラス型オブジェクトの生成を回避 ・ラムダ式の展開式に気をつける ・メソッドをラムダ式に入れると展開時にデリゲートをnewする ・ラムダ式にメソッドを食わせる場合は大体ゴミが出る ・値型以外を使っても勿論ゴミは出る ・コアロジックなどループする箇所では注意 ・ラムダ式にメソッドを入れないのは現実的ではない

  20. その他の回避策 ・事前確保によりゲーム中のアロケートを避ける - 確保時に足りないから到達可能性を見る - 確保するから到達可能性が変わってしまう - GC発生してほしくない時は確保しなきゃいいじゃん理論 - あんまり現実的ではないかも。インゲーム全体を管理可能な

    規模ならアリ ・データクラスはクラスでなく構造体で作る、とか。
  21. クラス型にする? それとも参照型? ・複製を行わないデータは構造体で作ると良い - 値型は代入時に複製が必要で、重くなり得る ・「クラスの継承」や「仮想メソッド」など多態性を使いたい場合はクラスで 作ると良い - というかクラスでしかできない

  22. その他、これはどうなの編 - foreachはだめ? - IL2CPPがfor文に展開してくれる - for文でボックス化が発生しないコードならOK - UniRxはだめ? -

    ふつーに使う分には問題ないと思います(内部コードは対策済 み) - オペレータの引数に入れるラムダ式がボックス化を誘発する コードの場合、展開時にボックス化すると思います
  23. その他、これはどうなの編 - LINQはだめ? - 完全にものによる・・・ - ラムダ式がある場合はラムダ式の展開式に注意 - 引数がある場合、値型で送るかジェネリック型で送れるなら ボックス化はしないはず

    - LINQの内部実装でボックス化してたら重いLINQメソッドなの で諦める
  24. まとめ - メモリの管理場所「スタック」と「ヒープ」を意識する - GCの発生条件を意識する - 「到達可能性」を変化させないことで間接的にGC発生率を減ら せる - 「new」「ボックス化」を抑える

    - ラムダ式は展開時にGCを誘発するケースに注意
  25. 参考にさせていただいたHP - https://www.slideshare.net/KMC_JP/c-91154309 - http://neue.cc/2016/01/06_525.html - https://www.scriptlife.jp/contents/programming/2017 /05/14/unite2017-performance/

  26. ご清聴 ありがとうございました