Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

C# ゲームプログラミングはホントにメモリのことに無頓着でいいの?

5,313 views

Published on

Unity や MonoGame など、C# でゲームを作る環境が整ってきた昨今。メモリ?なにそれ美味しいの?という初学者が、難しいことを考えずにモノづくりができる一方で、メモリについて知らないとトラブルになることもあります。C#でゲームプログラムを書いたことがある、くらいの方を対象に、メモリとは何か、から、メモリリーク・ガベージコレクションの話、そしてガベージコレクションの回数を減らす実装方法について話します。

Published in: Engineering
  • Be the first to comment

C# ゲームプログラミングはホントにメモリのことに無頓着でいいの?

  1. 1. 1 C#ゲームプログラミングは ホントにメモリのことに無頓着でいいの? KMC-ID:crys 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  2. 2. 2 自己紹介(C#er歴) • B1で勉強してまる8年 • B1、B2でC#製ゲームを3本制作 • B2-B4でバイト:C#/WPF、VB6の秘伝のソース • B4の研究、M1の学内ハッカソン、および インターンのゲーム制作でUnityを使用 • 仕事はUnityとか 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  3. 3. 3 C#、C++よりばり便利 入学直後、新歓期の雑談にて: 「C++っていう闇の言語があるんだけど… (黒魔術がヤバい的な話)」 「C++はメモリも自分で管理せなあかんくって。 C#はそのへん最後までいい感じにしてくれる」 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 当時の先輩
  4. 4. 4 当時のcrysさん 「(メモリとか知らないけど) C#すごい!!!!!!!」 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  5. 5. 5 C#で作った(学部時代) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 妖精さんに貢ぐ アクションパズル 妖精さんたちがお姫様を 守りながら進む 戦術シミュレーション 妖精さん等が導火線を つなげて消していく 対戦アクションパズル
  6. 6. 6 C#で作った(学部時代) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 妖精さんに貢ぐ アクションパズル 妖精さんたちがお姫様を 守りながら進む 戦術シミュレーション 妖精さん等が導火線を つなげて消していく 対戦アクションパズル 見よう見まね 「再帰よくわからん」 コード書いてない グラフィックと プランニング 私が 作りました
  7. 7. 7 ゲームがNFでトラブル 長時間プレイすると (メニュー画面とパズル画面を行き来すると) ゲームが重くなる 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 原因:メモリリーク。音声リソースの解放し忘れがあった XNA(MonoGameの前身)では、Loadしたテクスチャや音声波形は 明示的にDispose関数を呼ばないとメモリから解放されない →ゲームがどんどんメモリを使い潰していく! 私が 作りました メモリ メモリ メモリ
  8. 8. 8 ゲームがNFでトラブル 長時間プレイすると (メニュー画面とパズル画面を行き来すると) ゲームが重くなる 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 原因:メモリリーク。音声リソースの解放し忘れがあった XNA(MonoGameの前身)では、Loadしたテクスチャや音声波形は 明示的にDispose関数を呼ばないとメモリから解放されない →ゲームがどんどんメモリを使い潰していく! 私が 作りました メモリ メモリ メモリ 「C++はメモリも自分で管理せなあかんくって。 C#はそのへん最後までいい感じにしてくれる」
  9. 9. 9 ~生みの王子~ C#で書ける!!!!!!ゲームエンジン (Unityが流行り出した頃にはXNAは死んでた) 最高すぎていろいろ作った (院生時代) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys センサデバイスのモニターテスト用 アプリケーション コンソール向けゲーム全周画像ビューワ Not printed Not printed
  10. 10. 10 ~時を超えた処理落ち~ 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys シーンの動的読み込み・解放をしたら めちゃくちゃ(>0.5sec)処理落ちした 原因:ガベージコレクション(メモリの自動解放)がクソ重い Unityで使われているメモリ管理の仕組みが超☆前時代的で、 使っていたメモリを再利用可能にする処理が動くと 数フレーム単位でアプリの処理が止まる メモリ メモリ メモリ
  11. 11. 112018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  12. 12. 12 この講座の内容 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys メモリって何? C#におけるメモリ管理 ガベージコレクションとうまく付き合う 君のコードに潜むガベージコレクションの元凶 Unity編
  13. 13. 13 メモリって何? を している人には真新しい話はありません 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  14. 14. 14 メインメモリ 本講座ではこれ以降、単に 「メモリ」と言った場合は メインメモリを指します 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys ALU レジスタ (二次記憶)
  15. 15. 15 メモリ 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys ALU レジスタ  データを格納するためのマスが並んでいる  マスには番地が振られている  番地情報を通してデータを読み書きする 中身を拡大 100 101 102 103 104 105 106 107 108 109 110 ……
  16. 16. 16 メモリの確保と解放 メモリのサイズは有限⇒同じ場所を使い回したい ある番地に「まだ使うデータ」が置いてあるかどうかを 誰かが管理しておく必要がある さもなくば… 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 100番地に置いておこう 100番地使おうとしたら 何かあったわウマー 100 101 99 100 101 99
  17. 17. 17 メモリの確保と解放 メモリのサイズは有限⇒同じ場所を使い回したい ある番地に「まだ使うデータ」が置いてあるかどうかを 誰かが管理しておく必要がある 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 100番地 確保 してます 超越的存在 100 101 99
  18. 18. 18 メモリの管理方法 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  19. 19. 19 スタック メモリ上の隣接した番地に積み上げ・積み降ろすように データを配置・解放していくデータ管理方式 ✓ 配置の効率がよい ✗ データがいつ使われなくなるかが明確でないと使えない ✗ 積めるサイズは有限 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys プログラムの関数呼び出しでは、より新しく作った変数ほど より早く使わなくなるので、スタックで管理できる
  20. 20. 20 ヒープ データの大きさや、確保・解放の順序に依存することなく データを管理できる方式 ✓ スタックで管理できないデータを管理できる ✗ 使っている場所や大きさをすべて把握しておく必要がある ✗ 新たに空き領域を探す手間がある 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys C言語では malloc (C++ なら new 等) でメモリを確保したときは ヒープにデータを置く
  21. 21. 21 ヒープは管理が面倒 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys ヒープのどの領域が使われているかはOSが管理 確保と解放は利用者 (プログラム)の責任 利用者が解放し忘れるとOSに管理され続ける メモリ リーク 逆に二重に解放しても問題になる…
  22. 22. 22 C#におけるメモリ管理 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  23. 23. 23 ガベージコレクション (GC) C#(の実行環境)は ヒープ上の使われなくなったメモリ領域 を自動的に解放する 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys まかせた
  24. 24. 24 ガベージコレクション (GC) C#(の実行環境)は ヒープ上の使われなくなったメモリ領域 を自動的に解放する ついでに空き領域を詰める (コンパクション) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  25. 25. 25 GCと手動メモリ管理の比較 GC メモリリークしないので、 管理が楽 解放できる領域の探索に 時間がかかる 手動メモリ管理 確保・解放する領域を すべて指定しているので GCよりも高速・低負荷 メモリリークに注意 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  26. 26. 26 …ん? ←こいつメモリリークしたじゃん 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 私が 作りました ∵ C#実行環境の外側にある機能を使用するときは、 OSのメモリを確保する必要がある!! OSの管理しているメモリ C#実行環境 音を鳴らす機能で 確保したメモリ あの機能 使いたい 御意 音を鳴らす 機能 ブヒィィッ
  27. 27. 27 C#でメモリリークしないためには… • C#が管理していないメモリ(アンマネージドメモリ)を 利用するクラスが使わないか、ドキュメントを見る • IDisposable が実装されているクラスのインスタンスで 確実に Dispose 関数を呼ぶ実装をする • using 節が使えるなら使おう 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys using (var sr = new StreamReader(“example.txt”)) { // do something } えー
  28. 28. 28 Unityはメモリリークする? Unityの機能だけを使用していてもメモリリークする(それっぽい状態になる) アセットを解放しても参照されてるリソースが解放されない可能性 ↓↓ 以下を読んで(丸投げ) 【Unity】アセットのUnloadとDestroyについて – テラシュールブログ http://tsubakit1.hateblo.jp/entry/2016/10/02/232723 (あとは悪名高いアセットバンドルはアンロード忘れるとメモリリークするとか……) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  29. 29. 29 ガベージコレクションとうまく付き合う 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  30. 30. 30 GC、実際重い C#の処理系に依るが、環境によっては メインの処理を止めて、 一気にインスタンスを破棄したりする 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys だと2フレームくらい 平気で持ってかれるかも…
  31. 31. 31 ガベージ コレクションの条件 https://docs.microsoft.com/ja-jp/dotnet/standard/garbage-collection/fundamentals#conditions_for_a_garbage_collection GCはいつ発生するの? 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys システムの物理メモリが少ない場合。 • OS からのメモリ不足通知またはホストによって示されたメモリ不足のいずれかによって検出されます。 オブジェクトによって使用されているメモリが、許容されるしきい値を超える場合。 • このしきい値は、プロセスの進行に合わせて絶えず調整されます。 GC.Collect メソッドが呼び出された場合。 • ほとんどの場合、ガベージ コレクターは継続して実行されるため、このメソッドを呼び出す必要は ありません。このメソッドは、主に特別な状況やテストで使用されます。 GCは突然に
  32. 32. 32 GC、実際重い (再掲) C#の処理系に依るが、環境によっては メインの処理を止めて、 一気にインスタンスを破棄したりする 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys 弾幕をギリギリで避けてる時に2フレーム止まったらどうする???
  33. 33. 332018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  34. 34. 34 GCを根源から断ち切るには? 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys ヒープを使わない…ことは難しい • クラス型のインスタンスや配列はヒープに確保される • フレームワーク側の機能がクラス型で実装されていることも多い プレイアブルなシーンの間、GCを発生させないようにできないか? • GCの発生条件は「メモリが、許容されるしきい値を超える場合」… ⇒ヒープに新たにメモリを確保しようとしなければ、 GCの発生は起きない
  35. 35. 35 メモリ確保が発生しないプログラミング (1/2) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys class GoodScene { private Bullet[] bullets; public GoodScene() { bullets = new Bullet[256]; } public void MainLoop() { bullets[index].position = ... } } class BadScene { private List<Bullet> bullets; public BadScene() {} public void MainLoop() { var b = new Bullet(); bullets.Add(b) b.position = ... } } シーン開始時のロードで、必要な情報をすべてメモリに載せる
  36. 36. 36 メモリ確保が発生しないプログラミング (2/2) 意図せぬメモリ確保がないかチェックする 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys Visual Studio の 診断ツール Unity の Profiler
  37. 37. 37 君のコードに潜む ガベージコレクションの元凶 Unity編 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  38. 38. 38 OnGUI void OnGUI(){ GUI.Label(new Rect(0,0,32,32),”TEST”)); } 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys GUIクラスを使用して画面描画を行うと、 毎フレーム、ヒープへのメモリ確保が行われる GUIは開発・デバッグ機能という認識。製品ではuGUIなどへ移行を
  39. 39. 39 文字列演算 string はクラスなので、定義するとメモリに確保される (回避方法なし) 文字列の結合(+演算子)は、結合後の新しい文字列としてメモリに確保される StringBuilder を使用すれば、メモリの確保を抑えることができる 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys string str = “点数は” + score.ToString() + “点です”; StringBuilder sb = new StringBuilder(); void Update(){ sb.Append(“点数は”); sb.Append(score); sb.Append(“点です”); string str = sb.ToString(); // メモリ確保はどうしても起きる }
  40. 40. 40 List 動的に配列のサイズを変えたくてListを使った?甘えんな!! 当然メモリの確保が行われる。 固定長配列使え固定長配列 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys // List<int> intList で定義している intList.Add(3); int[] intArray = new int[256];
  41. 41. 41 LINQ LINQめっちゃ使いたいけどメモリアロケーションあるねん… for ループ回して配列なめてください… 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys using System.Linq; ... // int[] intArray で定義している intArray.Count(i => i == 3);
  42. 42. 42 Coroutine (1/3) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys IEnumerator TimerCoroutine() { // 300フレーム待つ for(int i=0; i < 300; i++){ yield return new WaitForEndOfFrame(); } // do something } StartCoroutine(TimerCoroutine());
  43. 43. 43 Coroutine (2/3) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys var waitForEndOfFrame = new WaitForEndOfFrame(); IEnumerator TimerCoroutine() { // 300フレーム待つ for(int i=0; i < 300; i++){ yield return waitForEndOfFrame; } // do something } StartCoroutine(TimerCoroutine());
  44. 44. 44 Coroutine (3/3) 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys var waitForEndOfFrame = new WaitForEndOfFrame(); IEnumerator TimerCoroutine() { // 300フレーム待つ for(int i=0; i < 300; i++){ yield return waitForEndOfFrame; } // do something } StartCoroutine(TimerCoroutine()); そもそもコルーチンを 生成すると メモリ確保される… コルーチンなしで頑張る…?
  45. 45. 45 などなど 詳しい情報は「Unity GC Alloc」みたいなのでググって うにばな メモリ使用量の最適化・ヒープメモリ ほか – livedoor Blog http://blog.livedoor.jp/akinow/archives/52474053.html ↑ただし、foreach で List を回すとメモリ確保する(クソ)仕様は Unity 5.5 で修正済 neue cc - C#のGCゴミとUnity(5.5)のコンパイラアップデートによるListの foreach問題解決について http://neue.cc/2016/08/05_537.html 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  46. 46. 46 まとめ 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys
  47. 47. 47 まとめ C#ゲームプログラミングでも メモリのことを気にしてあげる必要がある 2018/3/19 春合宿2018講座;C#ゲームプログラムとメモリ;crys アクションゲームのリアルタイム性を損ねないために • ゲームシーン中にヒープからメモリ確保をしないこと • 専用ツールでメモリの使用量を見るべし

×