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
What the flash - Photography Introduction
edds
64
11k
Done Done
chrislema
178
15k
Six Lessons from altMBA
skipperchong
22
3k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
13
8.3k
jQuery: Nuts, Bolts and Bling
dougneiner
59
7.2k
Art, The Web, and Tiny UX
lynnandtonic
290
19k
Java REST API Framework Comparison - PWX 2021
mraible
PRO
19
6.9k
The Pragmatic Product Professional
lauravandoore
26
5.8k
The MySQL Ecosystem @ GitHub 2015
samlambert
244
12k
Faster Mobile Websites
deanohume
300
30k
Infographics Made Easy
chrislema
238
18k
Facilitating Awesome Meetings
lara
43
5.6k
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/
ご清聴 ありがとうございました