【Unity】「ロード後も重い」を突き止める:Memory ProfilerとSnapshot比較

作成: 2026-07-16

フレームレートは足りているのに実機で突然落ちる、シーンを戻ってもメモリが戻らない——それはGCスパイクではなく常駐メモリの問題です。Memory ProfilerパッケージによるSnapshotの撮り方、Unity Objectsビューでテクスチャ・Mesh・AudioClipを読む方法、Before/After比較で「シーンを戻っても残るもの」を突き止める捜査手順、端末ごとのメモリ予算の考え方まで、実機クラッシュの原因調査を一本道で解説します。

Profilerの記事 でCPUのスパイクは追えるようになった。フレームレートも足りている。なのに——実機で10分遊ぶと突然アプリごと落ちる。ステージから戻ってもメモリ使用量が戻らない。ロード画面のたびに端末が熱くなる。

それは 「速さ」ではなく「重さ」の問題、つまり常駐メモリの世界です。この記事では Memory Profiler パッケージを使って、Snapshotの撮り方から、Before/After比較で「居座るメモリ」を突き止める捜査手順、端末ごとのメモリ予算の考え方までを一本道で解説します。

メモリの中身を調査するイメージ

この記事でわかること

  • GCスパイク(カクつき)と常駐メモリ(重さ・クラッシュ) の切り分け
  • Memory Profilerの導入と Snapshotの撮り方(実機接続)
  • Unity Objectsビュー でテクスチャ・Mesh・AudioClipを読む
  • Before/After比較 で「シーンを戻っても残るもの」を暴く
  • 端末から逆算する メモリ予算 の考え方
  • 実践:実機で落ちるステージから100MBを取り戻す

動作確認環境: Unity 2022.3 LTS / Unity 6・Memory Profiler 1.1+

Sponsored

「GCスパイク」と「常駐メモリ」は別の病気

メモリの問題は、大きく2種類に分かれます。症状も道具も対処も違うので、最初に切り分けます。

GCスパイクと常駐メモリ増加の別グラフ比較。左はフレーム時間のグラフに周期的なトゲが立つGCスパイク、右はメモリ使用量が階段状に増えてシーンを戻っても下がらない常駐メモリの漏れ
  • GCスパイク: 毎フレーム生まれては捨てられる短命なゴミ(文字列連結、newの乱発)が溜まり、回収の瞬間に カクッと止まる。道具は通常のProfiler、対処は GC最適化の記事 の領域です
  • 常駐メモリ: テクスチャ・Mesh・オーディオなどが読み込まれたまま 居座り続ける。症状はアプリ全体の重さ、ロードの遅さ、そしてモバイルでは OSによる無言の強制終了 です。クラッシュログすら残らないことが多く、「実機でだけ、忘れた頃に落ちる」という一番タチの悪い形で現れます

この記事が相手にするのは後者です。フレームレートのグラフをいくら眺めても常駐メモリは見えません。専用の道具——Memory Profilerが要ります。

Memory Profilerを入れて、Snapshotを撮る

Memory Profiler はPackage Manager(Unity Registry)から Memory Profiler を追加すると、Window > Analysis > Memory Profiler で開けます。

通常のProfilerが「毎フレームの流れ」を見る動画だとすれば、Memory Profilerは 「その瞬間のメモリの中身ぜんぶ」を写す写真(Snapshot) です。使い方は3手順です。

  1. 実機のDevelopment Buildを起動して接続する: ビルド設定でDevelopment Buildを有効にしてビルドし、実機で起動。Memory Profilerウィンドウ上部の接続先ドロップダウンで、Editorではなく 実機のPlayerを選びます
  2. Captureボタンで撮る: 数秒かかってSnapshotが保存されます
  3. 調べたい場面ごとに撮る: タイトル画面で1枚、重いステージで1枚、ステージから戻って1枚——比較こそがこの道具の本体なので、 場面の「前後」で撮る のが基本動作です

注意: Editorに接続して測った数値は参考になりません。Editorはエディタ自身の管理データやSceneビューのために、実際のゲームの何倍もメモリを使っています。「実機で落ちる」問題は、必ず実機のPlayerに接続して調べてください。

読み方:何が場所を食っているのか

Snapshotを開いたら、まず Summaryビュー で全体の内訳を見ます。だいたいのゲームで、主役は次の顔ぶれです。

項目中身太りやすさ
Texture2Dテクスチャ。UI・スプライト・モデルの絵★★★(ほぼ主犯)
Mesh3Dモデルの形状データ★★
AudioClipBGM・効果音★★(設定次第で激変)
Managed HeapC#のオブジェクト(List、クラスのインスタンス等)★(漏れの温床)
Graphics / RenderTexture描画用のバッファ類

次に Unity Objectsビュー に切り替えて、 サイズの降順に並べます。これがメモリ捜査の基本姿勢です——「何百個の小物」より「上位10個の大物」が勝負を決めます。2048×2048の非圧縮テクスチャ1枚は約16MB。同じ絵でも圧縮フォーマット(モバイルならASTCなど)が効いていれば数MBで済みます。上位に並ぶテクスチャの名前とサイズを見るだけで、「このタイトルロゴ、4Kのままだ」のような発見が始まります。

Sponsored

Before/After比較:戻っても残るものを暴く

Memory Profilerの真価は 2枚のSnapshotの比較(Compare) にあります。狙い撃ちたいのは、こういう質問です——「ステージから戻ったのに、なぜメモリが戻らないのか?」

Snapshot差分による捜査フロー図。タイトル画面でSnapshot Aを撮り、重いステージへ行って戻ってからSnapshot Bを撮り、比較ビューでBにだけ残っているアセットを漏れの容疑者として調べる

手順は儀式のように固定します。

  1. タイトル画面でSnapshot A を撮る
  2. 重いステージへ行き、ひと通り遊んで、 タイトルへ戻る
  3. 戻った直後に Snapshot B を撮る
  4. Memory Profilerで2枚を開き、 Compareモード で差分を見る

理屈のうえでは、AとBは同じタイトル画面なので中身はほぼ同じはずです。 Bにだけ残っているもの——それが「シーンを戻っても解放されなかった」容疑者リスト です。ステージのボステクスチャがタイトルに戻っても居座っていたら、何かがそれを掴んでいます。定番の犯人は4人です。

  • staticフィールドの参照: static List<Enemy> に入れたまま忘れた敵のデータ
  • 解除し忘れたイベント購読: 破棄されたはずのUIが、staticイベント経由でアセットを間接的に掴んでいる(イベントの記事 の解除ルールはメモリの話でもあります)
  • DontDestroyOnLoadのオブジェクト: シーンをまたいで生き残る設計のものが、ステージ専用アセットまで道連れに保持している
  • Releaseしていないアドレサブル: Addressablesの記事 の「借りたら返す」を破っているハンドル

比較ビューで容疑者を選ぶと、 何がそれを参照しているか(References) を辿れます。「誰が掴んでいるのか」まで特定できれば、修正は一直線です。

メモリ予算:端末から逆算する

漏れを塞いだら、次は「そもそも何MBまでならいいのか」です。答えは端末側にしかありません。モバイルのOSは、空きメモリが逼迫すると 警告なしでアプリを強制終了します。PCと違って「重いけど動く」が存在しない世界です。

現実的な方法は、 ターゲット最低スペックの実機を1台決めて、そこで落ちない上限を計測し、それをカテゴリに配る ことです。

端末のメモリ予算をカテゴリ別に配る図。実機で計測した安全ラインを財布に見立て、テクスチャ・オーディオ・Mesh・Managed Heap・余白へ配分する

配分そのものはゲームによりますが、考え方は共通です。

  • 一番大きな財布はテクスチャに(そして一番削りやすいのもテクスチャ。解像度と圧縮設定で数分の一になります)
  • 「余白」を必ず残す: シーン切り替えの瞬間は、新旧両方のアセットが同時に載る山場です。予算いっぱいまで使う設計は、ロードの瞬間に死にます
  • 予算はチェック表で運用する: 「同じ端末・同じ手順・同じ場面」でSnapshotを撮り、修正前後の数値を並べて記録します。測り方が毎回違うと、改善したのか誤差なのか永遠に分かりません

実践:落ちるステージから100MBを取り戻す

架空だけれど毎日どこかで起きている事件を、通しで捜査してみましょう。 「ステージ3だけ、古い端末で高確率で落ちる」。フレームレートは正常。ログには何も残っていない——常駐メモリの典型的な現場です。

メモリ捜査の場面図。Unity Objectsビューのサイズ順リストの最上位に巨大なテクスチャのブロックが並び、調査員のクレイ人形が虫眼鏡でそれを見つけている
  1. 実機Playerに接続し、ステージ3の直前と直後でSnapshotを撮る。直後の1枚のSummaryを見ると、Texture2Dが全体の6割を占めている
  2. Unity Objectsビューをサイズ降順に。最上位に見覚えのない BG_Stage3_Sky が48MB——4096×4096・非圧縮・しかもmipmapまで付いている。次点に、ほぼ同サイズの BG_Stage3_Sky_old(使っていない旧版まで参照されて載っている)
  3. Import設定を修正する: 空のテクスチャに4096は要らないので2048へ、圧縮をASTCへ、UI用ならmipmapをオフに。旧版はそもそも参照を断つ。この2枚だけで約90MBが消える
  4. 同じ手順でもう一度Snapshotを撮り、Compareで確認: Texture2Dの合計が下がったこと、消したはずの旧版が本当に消えたことを、目で確かめる
  5. 実機でステージ3を3周して、落ちないことを確認。チェック表に「修正前348MB → 修正後251MB・3周クラッシュなし」と記録して事件解決です

ポイントは2つ。 捜査は常にサイズの大きい順(上位10件を疑うだけで大半の事件は解決します)。 修正の確認まで同じ端末・同じ手順で(測り方を揃えて初めて、数値は証拠になります)。

おまけ:先に知っておくと良いこと

  • AudioClipは設定で激変する: 長いBGMの Load TypeDecompress On Load になっていると、圧縮を解いた生データがまるごとメモリに載ります(数十MB級)。BGMは Streaming にするのが定番です
  • 「読み込んだだけ」のResourcesに注意: Resources.Load したアセットは、参照が切れても Resources.UnloadUnusedAssets()(シーン遷移時に自動実行)まで残ることがあります。手動で大きなアセットを読むなら、手動で片付ける意識を
  • テクスチャ予算はプラットフォーム別に: Import設定のプラットフォーム別オーバーライドで、モバイルだけ解像度と圧縮を落とせます。PC向けの見た目を保ったままモバイルの予算に収める正攻法です。詳しくは モバイル最適化の記事

まとめ

  • カクつきは GCスパイク、実機の重さとクラッシュは 常駐メモリ。別の病気、別の道具
  • Memory Profilerは その瞬間の写真(Snapshot)。必ず 実機のPlayerに接続 して撮る
  • 読み方の基本は Unity Objectsビューをサイズ降順。主犯はほぼテクスチャ
  • Before/After比較 で「戻っても残るもの」を暴く。犯人はstatic参照・イベント解除忘れ・DontDestroyOnLoad・未Release
  • 予算は ターゲット実機で計測して逆算。余白を残し、チェック表で修正前後を比べる

あなたのゲームのいちばん重いシーン、Texture2Dの上位10件に、いま何が並んでいるか言えますか? 言えなければ——Snapshotを1枚撮るところから始めましょう。