プロセス間のメモリ割り当て

Android プラットフォームは、空きメモリが無駄なメモリであるという前提で動作しています。常に利用可能なメモリをすべて使用しようとします。たとえば、システムは、アプリが閉じられた後もメモリに保持するため、ユーザーはすぐに元のアプリに切り替えることができます。このため、Android デバイスは、ほとんどの場合、空きメモリが非常に少ない状態で動作しています。メモリ管理は、重要なシステム プロセスと多くのユーザー アプリケーション間で適切にメモリを分配するために不可欠です。

このページでは、Android がシステムとユーザー アプリケーションにメモリを割り当てる方法の基本について説明します。また、オペレーティング システムがメモリ不足の状況に対応する方法についても説明します。

メモリの種類

Android デバイスには、RAM、zRAM、ストレージの 3 種類のメモリがあります。 なお、CPU と GPU は同じ RAM にアクセスします。

メモリの種類

図 1. メモリの種類 - RAM、zRAM、ストレージ

  • RAM は、最も高速なメモリですが、通常は限られたサイズしかありません。通常、ハイエンド デバイスには最大量の RAM が搭載されています。

  • zRAM は、スワップ領域に使用される RAM のパーティションです。zRAM に入れられるとすべて圧縮され、zRAM からコピーされると圧縮解除されます。RAM の一部であり、そのサイズはページが zRAM に入ると大きくなり、zRAM から外れると小さくなります。デバイス メーカーはその最大サイズを設定できます。

  • ストレージには、ファイル システムや、すべてのアプリ、ライブラリ、プラットフォームのオブジェクト コードなど、すべての永続データが入っています。ストレージには、他の 2 種類のメモリよりもはるかに多くの容量があります。Android では、他の Linux 実装とは違い、ストレージがスワップ スペースに使用されません。これは、頻繁に書き込みを行うとこのメモリが摩耗し、ストレージ メディアの寿命が短くなる可能性があるためです。

メモリページ

RAM はページに分割されます。各ページは通常 4 KB のメモリです。

ページは、空いているか、使用中かのどちらかです。空きページは使用されていない RAM です。使用中ページは、システムでアクティブに使用されている RAM であり、次のカテゴリに分類されます。

  • キャッシュ済み: ストレージ上のファイルに基づくメモリ(たとえば、コードやメモリマップされたファイルなど)。キャッシュ済みメモリには、次の 2 種類があります。
    • 非公開: 1 つのプロセスで所有され、共有されていません。
      • クリーン: ストレージ上のファイルの変更されていないコピー。空きメモリを増やすために kswapd によって削除されることがあります。
      • ダーティ: ストレージ上のファイルの変更されたコピー。空きメモリを増やすために、kswapd によって zRAM に移動、または zRAM 内で圧縮されることがあります。
    • 共有: 複数プロセスで使用されています。
      • クリーン: ストレージ上のファイルの変更されていないコピー。空きメモリを増やすために kswapd によって削除されることがあります。
      • ダーティ: ストレージ上のファイルの変更されたコピー。空きメモリを増やすために、kswapd によって、あるいは msync() または munmap() を使用して、変更をストレージ上のファイルに書き戻せるようにします。
  • 匿名: ストレージ上のファイルに基づくことのないメモリ(たとえば、MAP_ANONYMOUS フラグを設定した mmap() によって割り当てられたものなど)。
    • ダーティ: kswapd によって zRAM に移動、または zRAM 内で圧縮される可能性があります。

空きページと使用中ページの割合は、システムが RAM をアクティブに管理するため、時間とともに変化します。このセクションで紹介したコンセプトは、メモリ不足の状況に対処するための鍵となります。このドキュメントの次のセクションで、さらに詳しく説明します。

メモリ不足への対処

Android には、メモリ不足の状況に対処するための主要なメカニズムが 2 つあります。カーネル スワップ デーモンとローメモリ キラーです。

カーネル スワップ デーモン

カーネル スワップ デーモン(kswapd)は Linux カーネルの一部であり、使用中メモリを空きメモリに変換します。このデーモンは、デバイスの空きメモリが少なくなったときにアクティブになります。Linux カーネルは、メモリ不足の下閾値と上閾値を管理しています。空きメモリ量が下閾値を下回ると、kswapd はメモリの回収を開始します。空きメモリ量が上閾値を上回ると、kswapd はメモリの回収を停止します。

kswapd はクリーンページを削除できます。これは、ストレージに基づくものであり、変更されていないからです。プロセスが削除されたクリーンページにアクセスしようとすると、システムはページをストレージから RAM にコピーします。このオペレーションはデマンド ページングとして知られています。

ストレージに基づく、削除されたクリーンページ

図 2. ストレージに基づく、削除されたクリーンページ

kswapd は、キャッシュ済み非公開ダーティページと匿名ダーティページを zRAM に移動でき、そこで圧縮されます。これにより、RAM 内の使用可能なメモリが解放されます(空きページ)。プロセスが zRAM 内のダーティページにアクセスしようとすると、そのページは圧縮解除され、RAM に戻されます。圧縮されたページに関連付けられたプロセスが強制終了されると、そのページは zRAM から削除されます。

空きメモリの量が一定の閾値を下回ると、システムはプロセスの強制終了を開始します。

zRAM に移動され圧縮されたダーティページ

図 3. zRAM に移動され圧縮されたダーティページ

ローメモリ キラー

多くの場合、kswapd は十分なメモリを解放してシステムに返すことができません。この場合、システムは onTrimMemory() を使用して、メモリが不足していて割り当てを減らす必要があることをアプリに通知します。これでも不十分な場合、カーネルはメモリを解放するためにプロセスの強制終了を開始します。これにはローメモリ キラー(LMK)を使用します。

強制終了するプロセスを決定するために、LMK は、oom_adj_score と呼ばれる「メモリ不足」スコアを使用して、実行中のプロセスに優先順位を付けます。スコアの高いプロセスから強制終了されます。バックグラウンド アプリは最初に削除され、システム プロセスは最後に削除されます。次の表は、LMK のスコア付けカテゴリを高いものから順に示しています。1 行目の最もスコアの高いカテゴリのアイテムが最初に強制終了されます。

Android プロセス(上が高スコア)

図 4. Android プロセス(上が高スコア、下が低スコア)

上記の表の各カテゴリについて説明します。

  • バックグラウンド アプリ: 以前に実行され、現在アクティブではないアプリ。 LMK は、まずバックグラウンド アプリを oom_adj_score が高いものから順に強制終了します。

  • 以前使用したアプリ: 最も最近使用されたバックグラウンド アプリ。バックグラウンド アプリよりも優先度が高く(スコアが低く)なります。ユーザーは、バックグラウンド アプリよりも、このカテゴリのアプリに切り替える可能性が高いためです。

  • ホームアプリ: これはランチャー アプリです。これを強制終了すると、壁紙が表示されなくなります。

  • サービス: サービスはアプリケーションによって開始され、クラウドへの同期またはアップロードが含まれる場合があります。

  • 知覚可能アプリ: 小さな UI を表示する検索プロセスの実行や音楽の再生など、なんらかの方法でユーザーが知覚できる非フォアグラウンド アプリ。

  • フォアグラウンド アプリ: 現在使用中のアプリ。フォアグラウンド アプリを強制終了すると、アプリケーションがクラッシュしたように見えるため、ユーザーはデバイスになんらかの問題が発生しているとわかります。

  • 永続(サービス): 電話や Wi-Fi などのデバイスのコアサービスです。

  • システム: システム プロセス。このプロセスが強制終了されると、スマートフォンが再起動したように見えます。

  • ネイティブ: システムで使用される非常に低いレベルのプロセス(たとえば、kswapd など)。

デバイス メーカーは、LMK の動作を変更できます。

メモリ フットプリントの計算

カーネルは、システム内のすべてのメモリページを追跡します。

さまざまなプロセスで使用されるページ

図 5. さまざまなプロセスで使用されるページ

アプリが使用しているメモリ量を判断するとき、システムは共有ページを考慮する必要があります。同じサービスまたはライブラリにアクセスするアプリ間では、メモリページが共有されます。たとえば、Google Play 開発者サービスとゲームアプリが位置情報サービスを共有しているとします。これにより、アプリケーションごとのメモリ量を判断するのが難しくなります。

2 つのアプリで共有されているページ

図 6. 2 つのアプリで共有されているページ(中段)

アプリケーションのメモリ フットプリントを判断するには、次の指標のいずれかを使用できます。

  • Resident Set Size(RSS): アプリが使用している共有ページと非共有ページの数
  • Proportional Set Size(PSS): アプリで使用されている非共有ページの数と、共有ページを均等に配分した数(たとえば、3 つのプロセスが 3 MB を共有している場合、各プロセスで 1 MB が PSS にカウントされます)
  • Unique Set Size(USS): アプリで使用されている非共有ページの数(共有ページは含まれません)

PSS は、ページが複数回カウントされないため、オペレーティング システムで、すべてのプロセスで使用されているメモリ量を知りたい場合に便利です。PSS は、共有するページとプロセスの数をシステムで調べる必要があるため、計算に時間がかかります。RSS は、共有ページと非共有ページを区別せず、計算が高速化されるため、メモリ割り当ての変化を追跡するのに適しています。

参考情報