リモートのクラウド Mac miniで Instruments によるメモリリーク調査
深夜2時、テスターからクラッシュ直前の状況が送られてきた。ある画面を十数回開閉しただけで、メモリ使用量が80MBから600MBまで一気に膨らみ、最終的にシステムに強制終了された。この「じわじわ増える型」のリークは、コードを目で追うだけではほぼ捕まえられず、Instrumentsに頼るしかない。問題は、チームのローカルMacが常にシミュレータやビルドタスクで埋まっていることだ。そこにさらにInstrumentsのサンプリングを走らせると、ファンが全開で回り出し、データもクリーンにならない。そこで、この一連の調査フローをクラウドのMac miniに移してみた。占有できる物理マシンなら他人にリソースを取られる心配がなく、サンプリングのノイズも明らかに少ない。今回はその具体的な手順をまとめる。
なぜ占有物理マシンで調査すべきか
Instruments自体、CPUとメモリへの負荷は決して小さくない。Leaksテンプレートはヒープスキャンをdefinitivamente周期的に実行し、Allocationsはすべてのアロケーションの呼び出しスタックを記録する。ホストが複数タスクで時分割される共有環境だと、サンプリングのタイムラインに無関係なスケジューリングのジッターが大量に混ざり込み、曲線が汚くなって「増加ポイント」を誤判定しやすくなる。占有物理マシンの利点は単純だ——観測されるメモリの跳ね上がりは基本的にすべて自分のコード側の操作に対応しており、「他人のタスクがリソースを食っているのでは」と最初に疑う必要がない。
メモリ問題の調査で一番怖いのは、手がかりが見つからないことではなく、偽の手がかりが山ほど見つかることだ——環境ノイズが大きいほど、偽の手がかりも増える。
事前準備:画面共有 + バージョン合わせ
クラウドMac miniを起動したら、まずシステム標準の画面共有(VNC)で接続する。解像度は最低でも1920x1080に設定すること。Instrumentsのタイムラインは低解像度だと詰まって見えてしまい、細部がまったく判別できない。
# ローカル端末で、まずSSHで接続確認しておく
ssh dev@<インスタンスのアドレス> "sw_vers -productVersion; xcodebuild -version"
# VNCクライアントで接続する。アドレスはインスタンスに割り当てられた内部/公開アドレス
open "vnc://dev@<インスタンスのアドレス>"
接続後、最初に確認すべきはXcodeのバージョンがローカルの開発機と一致しているかどうかだ。特にInstrumentsのテンプレートライブラリのバージョン差異により、一部の新機能(例えばSwift Concurrency関連のトレース)が欠落することがある。xcode-select -pでコマンドラインツールのパスが正しいことを確認し、Xcode → Open Developer Tool → Instrumentsを開く。
Leaksテンプレートでまずリーク箇所を特定する
新規Profileを作成し、デバイスを選択する(クラウドMac miniに接続した実機でも、あるいはシミュレータで先に再現してもよい)。テンプレートはLeaksを選ぶ。Leaksはバックグラウンドで周期的にヒープメモリをスキャンし、赤くマークされたオブジェクトは参照カウントがゼロになったにもかかわらず残存している孤立オブジェクトだ。
操作手順:
- 録画を開始し、アプリ内で疑わしい操作(例えばある画面を開閉する)を15〜20回繰り返す;
- 左側のLeaksトラックを観察し、赤いマークが出現するタイミングがどの操作に対応するかを確認する;
- 対応するLeakレコードを開くと、右側のExtended Detailにアロケーション時の呼び出しスタックが表示され、具体的なクラスとメソッドに直接ジャンプできる。
Leaksの限界は「確定的なリーク」しか報告しないことだ。NSTimerの強参照やクロージャによるselfキャプチャのような、参照チェーンは切れていないもの論理的には解放されるべきケースは、Leaksではほとんど捕まえられない。この場合はAllocationsに切り替える必要がある。
Allocationsで増加曲線を見る
新規Profileを作成し、テンプレートはAllocationsを選択、Record Reference Countsにチェックを入れる。同じ操作シーケンスを繰り返し、1周終わるたびに手動でMark Generationをクリックする。これによりタイムライン上にマーカーが打たれ、「各周の後、メモリが起点まで戻っているか」を比較しやすくなる。
読み方の要点:複数周の操作後にメモリ曲線が階段状に上昇し、各ステップの高さがほぼ一致している場合、ある固定オブジェクトまたは配列が毎回の操作で蓄積していると基本的に断定できる。"All Heap Allocations"ビューに切り替え、Growthでソートし、Generationとの比較を組み合わせれば、どのクラスのインスタンス数が安定的に増加しているかを直接確認できる。
| 観察項目 | Leaks | Allocations |
|---|---|---|
| クロージャ/Timerの循環参照を発見できるか | 弱い | 強い |
| 特定効率 | 速い、直接赤くマーク | 複数周の比較が必要 |
| 適用フェーズ | 初期スクリーニング | 増加源の精密特定 |
Memory Graphで裏付けを取る
Instrumentsで疑わしいクラスを特定したら、Xcodeに戻ってDebug Memory Graph(Xcodeデバッグバーのメモリアイコン)を、同じ操作シーケンスの後に実行する。Memory Graphはオブジェクトの参照関係図を描き出し、紫色のびっくりマークが循環参照の可能性を示す。このステップはInstrumentsの結論を二重に確認するもので、特に「誰が誰を強参照しているか」がはっきり見え、Instrumentsの呼び出しスタックよりも直感的にself.completion = { self.doSomething() }のようなクロージャキャプチャの問題箇所を特定できる。
修正後は即座に結論を出さず、Allocationsに戻って同じ操作シーケンスを再度実行し、Mark Generation間の曲線が水平になっている(階段状に上昇していない)ことを確認する。これでようやく修正が本当に有効だったと検証できる。
よくある落とし穴とチェックリスト
- 録画前には必ず一度「ウォームアップ」操作(起動、ログインなど一回限りのアロケーション)を行い、コールドスタートの正常なアロケーションをリークと誤判定しないようにする;
- ReleaseモードとDebugモードでメモリの挙動は完全には一致しないため、最終的な結論はRelease構成でも再確認する;
- クラウドMac miniで画面共有が切断・再接続されると、Instrumentsのセッションが中断される可能性がある。長時間の録画前には
caffeinate -iでシステムのスリープによるサンプリング中断を防いでおくとよい; - 修正のたびにAllocationsのGeneration比較法で回帰検証を行い、コードレビューだけで結論を出さないこと。
よくある質問
クラウド Mac miniで Instrumentsのプロファイリングは十分に動きますか?
動きます。Instruments自体もCPUとメモリを消費するため、他利用者と共有しない専有物理機のほうがサンプルが安定し、細かいタイムラインの読み取りに有利です。
画面共有の解像度が低いと作業に影響しますか?
タイムラインの視認性が落ちます。VNCは1920x1080以上に設定し、対象区間をInstruments側で拡大してから記録することを推奨します。
リーク特定後すぐに契約を終了してよいですか?
まだ早いです。同じインスタンス上で修正を適用し、アロケーション曲線が落ち着くのを確認したうえで、今の契約期間を終えるか判断してください。
コンソール://order
今すぐ使ってみたい方へ
専有物理Mac miniを、1日単位でレンタル。決済完了から約4分でご利用開始できます。