- React Nativeの仮想ドキュメントオブジェクトモデル(DOM)内にある、デタッチされたDOMのようなオブジェクト。
- JavaScriptオブジェクト内の循環参照。
- ガベージコレクションの妨げとなる、保持されたままのコンポーネントやイベントリスナー。
- 意図的にメモリリークを起こすサンプルのVega向けReact Nativeアプリを作成する。
- アプリの実行中のさまざまな段階でヒープスナップショットをキャプチャする。
- MemLabを使用してこれらのスナップショットを分析し、メモリリークを特定する。
- MemLabの出力を解釈して、リークの性質と原因を理解する。
前提条件
- Node.jsをインストールする。
-
MemLabをインストールする。
-
ANRのクラッシュ防止のため、デバッグvpkgビルドを使用するか、以下を追加して環境を構成します。
ANRクラッシュを防ぐように環境を構成する。そのためには、デバッグ用のvpkgビルドを使用するか、
react-native.config.jsに以下の設定を追加します。
手順1:サンプルアプリを作成する
次のコマンドを使用して、テスト目的で意図的にサンプルリークを起こすサンプルアプリを作成します。app.jsファイルの例
次のファイルには、メモリリークのシナリオを実行するサンプルアプリのコードスニペットと、メモリリークを作成、クリア、分析するツールが含まれています。手順2:アプリをビルドする
次のコマンドを実行します。手順3:アプリを実行する
-
Vega仮想デバイスを実行します。
-
Vega仮想デバイス上でアプリを実行します。
手順4:ヒープスナップショットをキャプチャしてコピーする
-
各スナップショットを取得する前に、ガベージコレクションをトリガーします。
- アプリの実行中に [Take Snapshot] ボタンをクリックして、デバイス上のヒープスナップショットをキャプチャします。
- [Create Detached DOM Leak] ボタンをクリックして、メモリリークを引き起こす要素を10,000個生成します。
- スナップショットをもう一度取得します。
- [Clear Leaks] ボタンをクリックして、メモリリークをシミュレートする要素を削除します。
-
最終的なスナップショットを取得します。
[Take Snapshot] ボタンをクリックすると、スナップショットファイルが保存されているシステム上の場所がログに記録されます。
- スナップショットファイルをローカルコンピューターにコピーします。 取得したヒープスナップショットを使用して、潜在的なメモリリークを分析して特定できます。
手順5:MemLabを実行する
スナップショットに対してMemLabを実行するには、次のコマンド形式を使用します。- すべてのスナップショットをメモリに読み込みます。
- ベースラインとターゲットのスナップショットを比較して、新しいオブジェクトを特定します。
- 特定した新しいオブジェクトを分析して、最終スナップショットまで存続しているかどうかを確認します。
- 保持パターンを理解するためにオブジェクト参照をトレースします。
MemLabコマンド構造の説明
memlab find-leaksコマンドは、メモリリークを特定するMemLabのコア機能です。
以下のパラメーターはオプションです。
- —baseline <初期のスナップショット> - 潜在的なリークが取り込まれる前に取得した、初期のヒープスナップショットを指定します。基準点として使用されます。
- —target <リーク後のスナップショット> - 潜在的なリークが取り込まれた後に取得したスナップショットを指定します。MemLabはこのスナップショットをベースラインと比較して、リークの可能性がある新しいオブジェクトを特定します。
- - リークのクリアを試みた後に取得したスナップショットを指定します。MemLabはこの情報を使用して、オブジェクトが本当にリークしているか、それとも正常なアプリ状態の一部であるかを判断します。
--trace-all-objects- このオプションは、すべてのオブジェクトをトレースするようにMemLabに指示します。これによってより包括的な分析が提供されますが、処理時間が長くなる可能性があります。
MemLabの出力例
MemLabの出力の解釈
MemLabの出力で重要な要素は以下のとおりです。Alive objects allocated in target page
このセクションには、アプリの操作後または変更後にまだメモリに存在しているオブジェクトが表示されます。この一覧は潜在的なメモリ保持の問題を示し、各オブジェクトについて次の詳細を含んでいます。name- オブジェクト名またはその構造型。type- オブジェクトがobject(オブジェクト:JSObject、JSArrayなど)であるか、closure(クロージャ:メモリを保持する関数)であるか。count- 各オブジェクト型のインスタンスの数。retainedSize- これらのオブジェクトによって保持されているメモリの合計。
JSArray (Object Type)(オブジェクト型)- 305個のインスタンスがあり、1.1MBのメモリが保持されています。JSObject(id, name)- 10,000個のインスタンスがあり、同じく1.1MBのメモリが保持されています。
Sampling trace due to a large number of traces
このセクションでは、10,100個のトレースがあるため、MemLabは49.5%のサンプリングを使用して効率的な処理を行ったことを確認できます。MemLab found X leaks
この例では、MemLabは5件のリークを発見しました。出力される一覧には、各メモリリークについて次の情報が含まれます。- 類似リークの数 - ツールが同様のリークパターンを検出した回数。
- リークオブジェクトの保持サイズ - リークのあったオブジェクトによって保持されているメモリの合計。
- リークのトレース - オブジェクトがどのように相互接続されているかと、ガベージコレクターがそれらを回収できない理由。
ガベージコレクション(GC)ルート
これらは、メモリに残っていて、ほかのオブジェクトのガベージコレクションを妨げているオブジェクトのエントリポイントです。このトレースは、GCルートが参照を通じてどのようにオブジェクトを存続させているかを、JSObjects、JSArray、クロージャー参照へと戻るパスと共に示します。 例1 リークパターン:4,956件の同様のリークがあり、1.1MBのメモリが保持されています。JSObject(current) → JSArray → JSObject(id, name)
このトレースは、JSArrayが10,000個のJSObject(id, name)オブジェクトへの参照を保持していて、これらのオブジェクトのガベージコレクションを妨げていることを示します。
例2
リークパターン:22件の同様のリークがあり、2.9KBのメモリが保持されています。
トレースには、React Nativeのビューシステムの一部であるDetached FiberNode RCTViewオブジェクトが含まれています。onBlurのようなイベントハンドラでは、コードでオブジェクトを削除した後も不要なオブジェクトが保持されるため、リークが発生する可能性があります。
MemLabによるスナップショットの分析
- Alive objects allocated in target pageセクションを調べて、保持されているオブジェクトを確認します。
-
「MemLab found [X] leak(s)」という行で、MemLabによって明確に特定されたリークの数を確認します。
各リークについて以下を確認します。
- 同様のリークの数と合計保持サイズを確認します。
- オブジェクトのトレースを調べて、システムがどのようにオブジェクトを保持しているかを把握します。
- デタッチされたDOM要素や大きい配列など、想定外の保持に注意します。
リークへの対処
アプリ内のリークに対処するには、次の操作を実行します。- リークのあるオブジェクトのパターン(デタッチされたDOM要素、クリアされないイベントリスナーなど)を特定します。
- アプリ内で対応するコードを特定します。
- 修正を実装します。たとえば、コンポーネントを適切にマウント解除し、配列をクリアし、イベントリスナーを削除します。
修正の確認
修正されたことを確認するには、次の操作を実行します。- 修正を実装したアプリを再ビルドし、再実行します。
- スナップショットの取得と分析のプロセスを繰り返します。
- 以前に特定されたリークがMemLabの出力に表示されなくなったことを確認します。
関連トピック
- コミュニティ:アプリのメモリが不足しているかどうか、どうすれば確認できますか?
- アプリのKPIを測定
- UIレンダリングの問題の特定
- オーバードローの検出
- Vega向けReact Nativeアプリでのコンポーネントの再レンダリングに関する問題の調査

