Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Gotanda.unity #10 メモリと闘う者達 〜GC編〜
Search
Nozomi Tanaka
January 23, 2019
0
2.9k
Gotanda.unity #10 メモリと闘う者達 〜GC編〜
21Pに誤記
誤:クラス型にする? それとも参照型?
正:クラス型にする? それとも値型?
誤記、どっちも同じ・・・
Nozomi Tanaka
January 23, 2019
Tweet
Share
More Decks by Nozomi Tanaka
See All by Nozomi Tanaka
Zenject Testing LT
barasixi
0
1.2k
Featured
See All Featured
Intergalactic Javascript Robots from Outer Space
tanoku
266
26k
Infographics Made Easy
chrislema
237
17k
10 Git Anti Patterns You Should be Aware of
lemiorhan
644
57k
Thoughts on Productivity
jonyablonski
57
3.7k
The Language of Interfaces
destraynor
150
22k
Building Flexible Design Systems
yeseniaperezcruz
317
37k
A better future with KSS
kneath
230
16k
A designer walks into a library…
pauljervisheath
199
23k
Building Your Own Lightsaber
phodgson
97
5.6k
Imperfection Machines: The Place of Print at Facebook
scottboms
257
12k
Testing 201, or: Great Expectations
jmmastey
27
6.3k
Debugging Ruby Performance
tmm1
68
11k
Transcript
メモリと闘う者達 〜GC編〜 Nozomi Tanaka / @nozomin770 Gotanda.unity #10 2019/1/23
Tanaka Nozomi(タナカ ノゾミ) ・ワンダープラネット株式会社 - グローバルスタジオ所属 ・Unity使い4年半、Cocos2d-xもはじめました ・Ruby、PHPなどを使ったサーバーエンジニアでもあります ・プロジェクトマネジメントとインフラの修行もはじめました ・Twitter:@nozomin770
アジェンダ ・1分でわかるメモリの仕組み ・C#とGC ・GCで死ぬ(フリーズする)ゲームを救えないのか ・ボックス化、ラムダ式、クラス型と構造体、愉快な仲間たち ・まとめ
これは、GCと戦う 熱き勇者達の物語である
1分で分かるメモリ管理 メモリには2種類の保存場所があります 「スタック」という場所と 「ヒープ」という場所の2つです
スタックのルール 先入れ、先出し、隙間なし 隙間なく積み上げるため効率がいい
ヒープのルール 空いているところにうまいこと入れる 隙間あり、効率はよくない
全部スタックだと効率がよいのでは? ・スタックが使えるときは限られている ・プリミティブ型はスタックを使えると思えばOK (object、stringは除く) ・その他のほとんどの場合はヒープのメモリを使うことになる
ヒープが悪い? そういうわけじゃない
C#のメモリとの付き合い方 ・C#はガベージコレクション(以下GC)という仕組みで メモリの片付けを自動管理してくれている ・GCが走るとき、UnityではGC終了まで処理が停止してしまう (俗に言う処理落ち)
GCの発生条件をつかもう ・OSの物理メモリが足りない場合 ・利用可能なヒープメモリの使用量が一定のしきい値を超えた場合 なおしきい値は常に変動する(!?) ・GC.Collectメソッドが呼び出された場合
GCが走る世界線を 回避できないのか・・・?
GCの動作原理を支配する ・C#のGCは「到達可能性」を見て回収可能な変数を見ます ・「到達可能性」の変動条件 →ヒープ領域へのメモリ確保(アロケート)が原因 →これは変数宣言時に行われる
到達可能性の変動を抑える ・ヒープ領域への確保が行われる条件 - ボックス化(ボクシング) - クラス型のオブジェクト生成 ・上記を回避するようにコードを書くことでGCが走らない世界線にたどり 着けるはずだ・・・
どうやって・・・
ボックス化とは ・値型をobject型(参照型)として扱うとき、スタック上からヒープ上に値を コピーする処理を「ボックス化」(boxing)と呼びます ・値型を使いながらもヒープ領域を使ってしまうため、(個々は僅かだが) 無駄に重くなる ・ヒープ領域へのアロケートが行われてしまう(GCを誘発する可能性が 生まれる)
ボックス化を(適度に)避けたい - メソッド引数がobject型になってないか気をつける - Equalsは生で使うとobject引数が多い… - 比較対象クラスへのIEquatable<T>インターフェース実装がお すすめ - 一部のUnityのクラスはDictionaryでボックス化が起きる
- structでないクラス - またはIEquatable<T>を実装していないクラス - UnityEngine.Objectを継承してるクラスに多い
とはいってもやりすぎは辛い ・ボックス化が起きる条件は値型の取り扱いのミスに多い ・int型をobject型に変換 ・ジェネリックは使ってOK ・(List<T>にintを入れてもboxingされない) ・GCが起きていい時はボックス化に気にする必要はない (と思ってます・・・)
クラス型オブジェクトの生成を回避 ・ラムダ式の展開式に気をつける ・メソッドをラムダ式に入れると展開時にデリゲートをnewする ・ラムダ式にメソッドを食わせる場合は大体ゴミが出る ・値型以外を使っても勿論ゴミは出る ・コアロジックなどループする箇所では注意 ・ラムダ式にメソッドを入れないのは現実的ではない
その他の回避策 ・事前確保によりゲーム中のアロケートを避ける - 確保時に足りないから到達可能性を見る - 確保するから到達可能性が変わってしまう - GC発生してほしくない時は確保しなきゃいいじゃん理論 - あんまり現実的ではないかも。インゲーム全体を管理可能な
規模ならアリ ・データクラスはクラスでなく構造体で作る、とか。
クラス型にする? それとも参照型? ・複製を行わないデータは構造体で作ると良い - 値型は代入時に複製が必要で、重くなり得る ・「クラスの継承」や「仮想メソッド」など多態性を使いたい場合はクラスで 作ると良い - というかクラスでしかできない
その他、これはどうなの編 - foreachはだめ? - IL2CPPがfor文に展開してくれる - for文でボックス化が発生しないコードならOK - UniRxはだめ? -
ふつーに使う分には問題ないと思います(内部コードは対策済 み) - オペレータの引数に入れるラムダ式がボックス化を誘発する コードの場合、展開時にボックス化すると思います
その他、これはどうなの編 - LINQはだめ? - 完全にものによる・・・ - ラムダ式がある場合はラムダ式の展開式に注意 - 引数がある場合、値型で送るかジェネリック型で送れるなら ボックス化はしないはず
- LINQの内部実装でボックス化してたら重いLINQメソッドなの で諦める
まとめ - メモリの管理場所「スタック」と「ヒープ」を意識する - GCの発生条件を意識する - 「到達可能性」を変化させないことで間接的にGC発生率を減ら せる - 「new」「ボックス化」を抑える
- ラムダ式は展開時にGCを誘発するケースに注意
参考にさせていただいた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/
ご清聴 ありがとうございました