趣味的なIT・ネットの話題

Unityでワンソースなシステム開発:Unityにおけるメモリ管理の方針

C# ガベージコレクション:宇宙仮面の C# Programming

Unityでのメモリ管理について色々とWeb上のドキュメントを読んでいたらこの記事がよく参照されているようです。じゃあ自分のプロジェクトではどの方針を採用するかということなのですが、かなり小規模なので、不要なリソースを自分で特定して任意のタイミングでGCを呼ぶという方針にしたいと思います。解放するリソースが増えそうだとusingを使う方針が良さそうですが。

実際のところでいえば一番大きなデータオブジェクトでも1Mbyteも無いはずなのでそれほど気にすることはなく、完全にMonoに任せるという方針もありかなとは思いました。ただOSサイドからするとMono環境も一種のアプリで、Monoが非Monoアプリのことまで考慮してメモリマネージメントをしているわけでは無いでしょうから(間違っていたら教えて下さい)、メモリを食い過ぎて非Monoアプリに迷惑をかけるような状況になるのもいかがなものかなと思われました。

そこで、
・明らかに不要と判断できるリソースは随時Nullを参照
・UIのタブ切り替えはユーザーにとって遅延を感じにくいタイミングなのでその時点でGCを実行
という方針を採用することにします。

NET TIPS ガベージ・コレクタを明示的に動作させるには?

こちらのコードを参考にして以下のコードでテストしてみました。

		Debug.LogFormat("before first GC {0,0:N0}", GC.GetTotalMemory (false));

		GC.Collect();

		long first = GC.GetTotalMemory (false);

		Debug.LogFormat("first memory {0,0:N0}", first);

		// c#ではcharは2バイトのUnicode文字 それを1000文字分の文字列だと2Kバイト
		// それを1000個分作ると2Mバイト
		string [] abc = new string[1000];
		for (int i = 0; i < 1000; i++ ) {
			abc[i] = new String('A',1000);
		}

		long diff1 = GC.GetTotalMemory (false) - first;
		Debug.LogFormat("now - first : after creation of array {0,0:N0}", diff1);

		long diff2 = GC.GetTotalMemory (false) - first;
		Debug.LogFormat("now - first : after setting nonreference {0,0:N0}", diff2);

		abc = null;

		// nullとしたあと直後にGC.Collectを呼んでもコレクションの対象にならない
		// 何でもいいので無駄なことをして時間を潰す
		for (int i = 0; i < 10000; i++ ) {
			i = i + 1;
		}

		System.GC.Collect(); // アクセス不可能なオブジェクトを除去
		System.GC.WaitForPendingFinalizers(); // ファイナライゼーションが終わるまでスレッド待機
		System.GC.Collect(); // ファイナライズされたばかりのオブジェクトに関連するメモリを開放

		long diff_first_now = GC.GetTotalMemory (false) - first;
		Debug.LogFormat("now - first : after Gc{0,0:N0}", diff_first_now);
		long diff_diff3_now =  diff_first_now - diff1;
		Debug.LogFormat("now - first - diff1: after Gc difference from array creation {0,0:N0}", diff_diff3_now);

結果はこちら。

スクリーンショット 2015-10-24 17.03.15
元のサンプルプログラムと違うのは、実行にあたってまず一度GCを実行していることです。このミニロジックが実行されるまでにある程度のガーベージが発生しているので、キレイにしておかないと実行結果が状況によって変わってしまいました。あとnullを設定した直後にCollectionを実行してもメモリが解放されませんでした。余りにも時間的に近接していると廃棄対象とみなしてくれないようです。とりあえずこのコードでは時間つぶしを入れています。

若干の不安定性はありますが、nullとすれば明示的にGCの対象とできそうです。ユーザーエクスペリエンス上遅延を感知しにくいタイミングが定期的に発生するプログラムなのであれば、その時点でGCを実行させると、ユーザーに違和感を感じさせにくいと思います。


Facebooktwitterpinterestlinkedinmail
納得したらすぐにシェア!