Java/Kotlin の割り当てを記録すると、パフォーマンス上の問題を引き起こす可能性がある望ましくないメモリパターンを特定できます。プロファイラは、オブジェクト割り当てに関して次の内容を表示できます。
- 割り当てられたオブジェクトの種類とオブジェクトのメモリ使用量。
- 各割り当てのスタック トレースと含まれているスレッド。
- オブジェクトの割り当てが解除された日時。
ユーザーによる通常の操作と極端な操作の際のメモリ割り当てを記録して、コードが短時間にオブジェクトを割り当てすぎている領域、またはメモリリークが発生するオブジェクトを割り当てている領域を正確に特定する必要があります。アプリのメモリをプロファイリングする理由についての記事もご覧ください。
Java/Kotlin の割り当てを記録する方法
Java/Kotlin の割り当てを記録するには、プロファイラの [ホーム] タブから[メモリ使用量をトラッキング(Java/Kotlin の割り当て)] タスクを選択します。Java/Kotlin の割り当てを記録するには、デバッグ可能なアプリ(プロファイラ: デバッグ可能なアプリとして実行(完全なデータ)を使用)が必要です。
Android Studio は、デフォルトでメモリ内のすべてのオブジェクト割り当てをキャプチャします。多数のオブジェクトを割り当てるアプリの場合は、プロファイリング中にアプリが目に見えて遅くなることがあります。プロファイリング中のパフォーマンスを改善するには、[Allocation Tracking] プルダウンに移動し、[Full] ではなく [Sampled] を選択します。サンプリングを行う場合、プロファイラはメモリ内のオブジェクト割り当てを定期的に収集します。
録画中にガベージ コレクション イベントを強制実行するには、ゴミ箱アイコン をクリックします。
Java/Kotlin 割り当ての概要
録画を停止すると、次の画面が表示されます。
- イベント タイムラインには、アクティビティの状態、ユーザー入力イベント、画面の回転イベントが表示されます。
- メモリ使用量のタイムラインには次の情報が表示されます。タイムラインの一部を選択すると、特定の時間範囲でフィルタできます。
- 各メモリカテゴリで使用されているメモリ使用量の積み上げ棒グラフ。左の Y 軸はメモリ使用量を、上部の色キーはメモリカテゴリを示しています。
- 割り当てられたオブジェクトの数を表す破線。右の Y 軸はオブジェクトの数を示しています。
- 各ガベージ コレクション イベントのアイコン。
- [テーブル] タブにクラスのリストが表示されます。合計数は、選択した期間の終了時点での割り当て数(割り当てから割り当て解除を引いた数)です。したがって、合計数の値が最も大きいクラスを最初にデバッグするとよいでしょう。選択した期間のピーク割り当てに基づいてクラスのトラブルシューティングを行う場合は、[割り当て] で優先順位を付けます。同様に、Remaining Size は、Allocations Size から Deallocations Size を引いた値(バイト単位)です。
- [表] リストでクラスをクリックすると、[インスタンス] ペインが開き、関連するオブジェクトのリストが表示されます。このリストには、オブジェクトが割り当てられた日時、割り当て解除された日時、シャロー サイズなどが含まれます。
[可視化] タブには、選択した期間中にコールスタック内のすべてのオブジェクトの集計ビューが表示されます。基本的には、表示されているインスタンスを含むコールスタックが使用するメモリの合計量が表示されます。最初の行にはスレッド名が表示されます。デフォルトでは、オブジェクトは割り当てサイズに基づいて左から右に積み重ねられます。ドロップダウンを使用して順序を変更します。
ヒープ プルダウンを使用して、特定のヒープにフィルタします。ヒープダンプのキャプチャ時に使用できるフィルタに加えて、JNI ヒープ内のクラスをフィルタすることもできます。JNI ヒープは、Java Native Interface(JNI)参照の割り当てと開放が行われる場所を示すヒープです。
割り当ての配置方法を選択するには、配置のプルダウンを使用します。ヒープダンプをキャプチャするときに使用できる配置に加えて、コールスタックで配置することもできます。
メモリをカウントする方法
上部に表示される数値は、Android システムに応じて、アプリがコミットしたすべてのプライベート メモリ ページに基づいています。このカウントには、システムまたは他のアプリで共有されるページは含まれません。メモリカウントのカテゴリは次のとおりです。
- Java: Java または Kotlin コードから割り当てられたオブジェクトのメモリ。
Native: C または C++ コードから割り当てられたオブジェクトのメモリ。
Android フレームワークでは、コードを Java または Kotlin で記述していても、画像アセットやその他のグラフィックを処理するときなどはネイティブ メモリを使用してユーザーのさまざまなタスクが処理されるため、アプリで C++ を使用していない場合でも、使用された一部のネイティブ メモリがここに表示される場合があります。
Graphics: GL サーフェスや GL テクスチャなど、画面にピクセルを表示するためにグラフィック バッファキューに使用されたメモリこのメモリは専用の GPU メモリではなく、CPU と共有されるメモリであることにご注意ください。
Stack: アプリのネイティブ スタックと Java スタックの両方によって使用されたメモリ。通常、アプリが実行しているスレッドの数に関係します。
Code: DEX バイトコード、最適化またはコンパイルされた DEX コードなど、コードとリソースのためにアプリが使用したメモリ。
so
ライブラリとフォント。Others: アプリによって使用された、カテゴリが不明なメモリ。
Allocated: アプリによって割り当てられた Java/Kotlin オブジェクトの数。C または C++ で割り当てられたオブジェクトはカウントされません。
割り当てレコードを検査する
割り当て記録を調べる手順は次のとおりです。
- [表] タブのクラスリストを参照して、割り当てまたは合計数の値が著しく大きく(最適化の対象によって異なります)、リークが発生している可能性があるオブジェクトを見つけます。
- [Instance View] ペインでインスタンスをクリックします。そのインスタンスに該当する内容に応じて、[フィールド] タブまたは [割り当てコールスタック] タブが開きます。[Fields] タブまたは [Allocation Call Stack] タブの情報を使用して、インスタンスが本当に必要かどうか、または不要な重複かどうかを判断します。
リストエントリを右クリックすると、関連するソースコードに移動します。
グローバル JNI 参照の表示
Java Native Interface(JNI)は、Java コードとネイティブ コードが互いに呼び出せるようにするフレームワークです。JNI 参照はネイティブ コードによって手動で管理されるため、次のような問題が発生する可能性があります。
- ネイティブ コードで使用される Java オブジェクトが長期間存在し続ける。
- JNI 参照が最初に明示的に削除されずに破棄されると、Java ヒープ上の一部のオブジェクトに到達不能となる可能性があります。
- グローバル JNI 参照制限を使い切った。
このような問題のトラブルシューティングを行うには、プロファイラで [JNI ヒープを表示] を選択して、すべてのグローバル JNI 参照を参照し、Java タイプとネイティブ コールスタックでフィルタします。[フィールド] タブでインスタンス フィールドを右クリックし、[インスタンスに移動] を選択して、関連する割り当て呼び出しスタックを表示します。
[Allocation Call Stack] タブには、コード内のどこで JNI 参照の割り当てと解放が行われているかが表示されます。
JNI について詳しくは、JNI に関するヒントをご覧ください。