パフォーマンスの測定と分析の例

以下の例では、システム トレースと Macrobenchmark、およびメモリ プロファイリングを使用して、特定の種類のパフォーマンス問題を測定、改善する方法を示します。

systrace を使用したアプリの起動のデバッグ

起動時間をデバッグする場合は、systrace ログの使用をおすすめします。systrace は、インストルメント化済みのコードを使用して、特定のイベントの発生に要する時間を出力するシステムです。このトレースを使用して、アプリ(またはシステム内の他のプロセス)で何が起こっているかを確認できます。Android プラットフォームと Jetpack ライブラリは、アプリ内の多くの重要なイベントに関するインストルメンテーションを備えており、その発生時にログを記録できます。また、独自のカスタム トレースでアプリをインストルメント化することもできます。このカスタム トレースは systrace の可視化ツールに一元的に表示されるため、アプリで起きていることの全体像を把握できます。

systrace または Perfetto の使用

systrace の基本的な使用方法については、Debugging Application Performance の動画をご覧ください。

起動時間を分析するには、まず起動中に何が起きているかを把握する必要があります。このページの説明よりも詳しい情報が必要な場合は、アプリの起動時間についてのドキュメントをご覧ください。アプリの起動プロセスの概要が説明されています。

アプリの起動には以下の段階があります。

  • プロセスを起動する
  • 汎用アプリ オブジェクトを初期化する
  • アクティビティを作成して初期化する
  • レイアウトをインフレートする
  • 最初のフレームを描画する

起動タイプには次の段階があります。

  • コールド スタート: これは、デバイスの起動後、あるいはユーザーまたはシステムによるアプリプロセスの強制終了後、最初にアプリを起動したときに起こります。これにより、保存済み状態を使用せずに新しいプロセスが作成されます。
  • ウォーム スタート: これは、アプリがすでにバックグラウンドで実行されているときに起こります。ただし、アクティビティを再作成してフォアグラウンドに移動する必要があります。アクティビティの再作成は、既存のプロセスを再利用する、または保存済み状態からプロセスを再作成する中で行われます。Macrobenchmark テスト ライブラリは、前者を使用して一貫したウォーム スタートアップ テストをサポートします。
  • ホットスタート: これは、プロセスとアクティビティが実行中で、フォアグラウンドに移動するだけで済む場合に起こります。必要に応じて、オブジェクトの再作成や、新しいフォアグラウンド アクティビティのレンダリングを行うこともできます。起動の最短シナリオです。

systrace のキャプチャには、開発者向けオプションにある、デバイス上でシステム トレースするアプリを使用することをおすすめします。コマンドライン ツールを使用する場合は、Android 10(API レベル 29)以上で Perfetto を使用できますが、それ以前のバージョンのデバイスでは systrace を使用する必要があります。

「最初のフレーム」という言葉は少し正確性に欠けます。なぜなら、最初のアクティビティを作成した後のアプリの起動処理は、大きく異なる場合があるからです。アプリによっては、数フレームにわたってインフレーションを続けるものもあれば、すぐに 2 番目のアクティビティを起動するものもあります。

可能であれば、アプリの観点で起動が完了したときに、reportFullyDrawn(Android 10 以降で使用可能)を呼び出すことをおすすめします。

システム トレースで注目すべき点を以下に示します。

モニター競合
図 1. モニター保護されたリソースの競合により、アプリの起動が大幅に遅れることがあります。

同期バインダー トランザクション
図 2. アプリのクリティカル パスに存在する不要なトランザクションを探します。

同時実行ガベージ コレクション
図 3. 同時実行ガベージ コレクションはよく起こることで、影響もそれほど大きくはありません。ただし、頻繁に目にする場合は、Android Studio の Memory Profiler で調査することをおすすめします。

起動時の I/O
図 4. 起動中の I/O に長い停滞がないかをチェックします。

図 4 では、同時に I/O を行っている他のプロセスが I/O 競合の原因となりうることに注目し、他のプロセスを実行しないようにします。

他のスレッドのアクティビティが UI スレッドの妨げになる可能性があるため、起動時のバックグラウンド処理に注意します。なお、デバイスによって CPU 構成が異なるため、デバイスが変わると同時実行できるスレッドの数も変わる可能性があります。

一般的なジャンク発生源に関するガイドもご覧ください。

Android Studio の Memory Profiler を使用する

Android Studio の Memory Profiler は、メモリリークや不適切な使用パターンによって引き起こされるメモリ負荷を軽減するための強力なツールです。これにより、オブジェクトの割り当てとコレクションをリアルタイムに確認できます。

アプリのメモリの問題を修正するために、Memory Profiler を使用して、ガベージ コレクションが発生する理由と頻度や、ヒープが増え続ける原因となるメモリリークの可能性を確認できます。

アプリのメモリをプロファイリングする手順は次のとおりです。

1. メモリの問題を検出する

メモリの問題を検出するには、アプリのメモリ プロファイリング セッションの記録を開始し、最終的にガベージ コレクション イベントをトリガーする、メモリ フットプリントが増加しているオブジェクトを見つけます。

オブジェクト数の増加
図 5. Memory Profiler に示された、時間の経過に伴うオブジェクト割り当て数の増加。

ガベージ コレクション
図 6. Memory Profiler に示された、ガベージ コレクション イベント。{.:image-caption}

メモリ負荷を増大させているユースケースを特定したら、根本原因の分析を開始します。

2. メモリ負荷のホットスポットを診断する

タイムラインで範囲を選択し、割り当てとシャローサイズの両方を可視化します。

割り当てとシャローサイズを可視化する
図 7. Memory Profiler に示された、タイムラインで選択した範囲における割り当てとサイズ。

このデータは、複数の項目で並べ替えを行えます。以下のセクションでは、各ビューを利用して問題を分析する例を示します。

クラスで並べ替える

クラスでの並べ替えは、キャッシュに保存(メモリプールから再利用)すべきオブジェクトを生成しているクラスを見つけ出す場合に便利です。

たとえば、アプリが「Vertex」というクラスのオブジェクトを毎秒 2,000 個作成していたとします。これにより、割り当て数が毎秒 2,000 増加することになり、クラスで並べ替えることでそれがわかります。その場合、ガベージの生成を避けるために、このようなオブジェクトを再利用すべきかどうか検討します。すべきという結論なら、メモリプールの実装が必要かもしれません。

コールスタックで並べ替える

コールスタックでの並べ替えは、メモリ割り当てのホットパス(ループ内部や大量の割り当て処理を行う関数など)がある場合に便利です。コールスタックで並べ替えたビューにより、このような割り当てのホットスポットがわかります。

シャローサイズと保持サイズの比較

シャローサイズがトラッキングするのは、オブジェクトそのもののメモリのみです。したがって、主にプリミティブで構成される単純なクラスのトラッキングに使用します。

それに対して保持サイズは、オブジェクトそのものに割り当てられたメモリと、そのオブジェクトのみが参照する他のオブジェクトに割り当てられたメモリの合計サイズを表します。そのため、プリミティブ フィールドだけでなく、他のオブジェクトの割り当ても必要とする複雑なオブジェクトによるメモリ負荷をトラッキングする場合に使用します。この値を取得するには、Memory Profiler を使用してメモリダンプを作成します。そのヒープに割り当てられたオブジェクトが追加で表示されます。

完全メモリダンプ
図 8. Memory Profiler ツールバーの「Java ヒープをダンプ」ボタンをクリックすると、いつでもメモリダンプを作成できます。

列として追加
図 9. メモリダンプを作成すると、そのヒープ内のオブジェクト割り当てを示す列が表示されます。

3. 最適化の効果を測定する

メモリ最適化による改善のうち測定が容易なものに、ガベージ コレクションがあります。最適化によりメモリ負荷が軽減されると、ガベージ コレクション(GC)が少なくなるはずです。これを測定するには、プロファイラのタイムラインで GC 間の時間を測定します。メモリ最適化後は、GC 間の時間が長くなるはずです。

上記のようなメモリ改善の最終的な効果は次のとおりです。

  • アプリに定常的なメモリ負荷の問題がなくなれば、メモリ不足の問題によってアプリが強制終了される頻度が低くなります。
  • GC を減らすことで、ジャンク指標が改善されます。これは、GC が CPU の競合を引き起こし、その間のレンダリング処理を遅延させる可能性があるものだからです。