メモリの一歩

Unityのゲーム開発においてメモリ不足は問題になりやすいです。
ただメモリ改善を行おうにも、何から始めていいか悩む人も多いかと思います。
ここでは、普段自分が行っているメモリ改善のその一歩を記したいと思います。

※本記事では具体的な改善方法には触れません。改善していくための手法がメインとなります

はじめに

まずはじめに言いたいのが

「開発段階でメモリ使用量を試算し、問題にならないように作る」

これが最も大事です。すでに出来上がったものに対して修正を加えるのは大変ですしバグのリスクもあります。なので、開発段階での適切な見積もりが重要です。

  • 想定されるTextureの総メモリサイズはどのくらいか?
  • 大量に読み込むリソースはあるか?
  • 頻繁にメモリ確保するオブジェクトはあるか?

その上でどうしても必要になった場合は、メモリ改善を行うことになるかと思います。
(特にソシャゲは更新頻度に対して改善が追いつかないことが多いかもです。。)

FPSMemoryFriends - 問題にならないように作る

f:id:okamura0510:20190531202947g:plain
【UnityAsset】FPSMemoryFriends – UnityでAndroid/iOSのFPSとメモリ使用量を取得する

適切な見積もりを行っていれば、開発中はそれほど問題はないかと思います。
ただメモリリークや仕様変更など不測の事態は発生します。
そんな時に、自分は上記アセットでメモリを視覚化して問題に気づけるようにしています。

MemoryProfiler - 問題の大枠を把握

f:id:okamura0510:20190531193256j:plain

メモリが肥大化してしまった場合、まずはざっくりと問題の大枠を把握するといいです。
そんな時に便利なのがUnity2018から新たに追加されたMemoryProfilerです。
これを使うと、ゲーム中のメモリがどのように使われているか分かりやすく表示出来ます。

導入手順

  • Unity2018.3以上を起動
  • 「Window>Package Manager」を表示
  • 「Advanced>Show preview packages」を選択(MemoryProfilerはまだpreview版)
  • Memory ProfilerのInstallボタンからインストール
  • PackageManagerを閉じ、「Window>Analysis>Memory Profiler」を表示
  • ゲーム実行中に「Capture Editor」でその瞬間のメモリ情報を保存
  • SnapshotのOpenボタンでメモリ情報を表示
  • メモリ情報内の領域を選択すると、さらに詳しい情報も表示出来る
    ※ちょっと動作が重いのと、一覧でメモリ消費が大きい順に見やすくしてくれると嬉しいなぁ

adb shell dumpsys meminfo - 問題の地点を把握

# 1秒おきにアプリのメモリを調べるコマンド
while true; do adb shell dumpsys meminfo {パッケージ名}; sleep 1s; done

f:id:okamura0510:20190531231310j:plain:w512

問題の大枠を把握したら、具体的にどの地点でメモリが増えているか調べます。
自分はそういう時にAndroidのadbコマンドで調べています(PCと実機で結果が変わりやすいので、実機で調べることが重要です)。
ProfilerやiOSのXcodeで調べてもいいのですが、Profilerを使うにはDevelopment Buildが必要だったり、iOSのビルドは時間が掛かったりするので、まずは簡単に調べられるadbコマンドを使用しています。

Profiler - 問題の詳細を把握

f:id:okamura0510:20190531201644p:plain

問題の地点が大体分かったら、その付近でのメモリの詳細をProfilerを使って調べます。
「Window>Analysis>Profiler」を表示し、「Deep Profile」を選択(DeepProfileを入れると、メソッド内のどこでメモリが消費されているかまで調べることが出来ます)。
問題の地点でProfilerのMemoryグラフを確認し、波形が高くなってるところでCPUグラフのフレームを選択。
下のTimelineをHierarchyに変更し、「GC Alloc」が大きい処理を確認。
これでメモリを大量に消費してる処理を特定出来ます。

Profiler.BeginSample~EndSample - 問題の部分を把握

public Monster()
{
    Profiler.BeginSample("[Monster.Init]");
    Init();
    Profiler.EndSample();
    
    PlayAnimation();
}

f:id:okamura0510:20190531204511p:plain

問題の詳細まで把握すれば、あとは改善していくだけですが、もう一歩踏み込みます。
Profiler.BeginSample~EndSampleでプログラムの特定部分を囲うと、Profilerにその部分の情報を表示させることが出来ます(TimelineやHierarchyにBeginSampleで指定したラベルが表示される)。
あとは粒度を細かく調整していけば、メモリ肥大化の原因を特定出来ると思います。

最後に

まぁ原因を特定できたとしても、直せるかどうかは別なんですけどね(´;ω;`)

参考
【CEDEC2018】一歩先のUnityでのパフォーマンス/メモリ計測、デバッグ術
【Unity】CPUプロファイラでパフォーマンスを改善する 前編
【Unity】CPUプロファイラでパフォーマンスを改善する 後編
Profilerの使い方と最適化の目安の付け方(前編)【Unity】【Unite 2017 Tokyo】【最適化】
Profilerの使い方と最適化の目安の付け方(後編)【Unity】【Unite 2017 Tokyo】【最適化】