Android Runtime (ART) と Dalvik 仮想マシンは、ページングとメモリ マッピング (mmapping) を使用してメモリを管理します。 つまり、アプリが変更したメモリは、新しいオブジェクトを割り当てた場合でも、mmapped ページに触れた場合でも、RAM に常駐し、ページング アウトすることはできません。 アプリからメモリを解放する唯一の方法は、アプリが保持しているオブジェクト参照を解放して、ガベージコレクタがメモリを利用できるようにすることです。 コードなど、変更せずにマップされたファイルは、システムがそのメモリを別の場所で使用したい場合、RAM からページアウトできます。
このページでは、Android がアプリ プロセスとメモリ割り当てを管理する方法を説明します。 アプリでメモリをより効率的に管理する方法の詳細については、アプリのメモリを管理するを参照してください。
ガーベッジ コレクション
ART や Dalvik 仮想マシンのような管理メモリ環境は、各メモリ割り当てを追跡します。 いったんメモリの一部がプログラムによって使用されなくなったと判断すると、プログラマーが介入することなく、ヒープに戻して解放します。 管理されたメモリ環境において未使用のメモリを回収する仕組みは、ガベージコレクションとして知られています。 ガベージ コレクションには 2 つの目標があります。将来アクセスできなくなるプログラム内のデータ オブジェクトを見つけることと、それらのオブジェクトによって使用されるリソースを取り戻すことです。
Android のメモリ ヒープは世代別であり、割り当てられるオブジェクトの予想寿命とサイズに基づいて、追跡する割り当ての異なるバケットが存在することを意味しています。 たとえば、最近割り当てられたオブジェクトは、若い世代に属します。
各ヒープ世代には、その世代のオブジェクトが占有できるメモリ量に専用の上限があります。
各ヒープ世代には、そこにあるオブジェクトが占有できるメモリ量に対する専用の上限があります。世代がいっぱいになり始めるといつでも、システムはメモリを解放するためにガベージ コレクション イベントを実行します。 ガベージ コレクションの期間は、どの世代のオブジェクトを収集しているか、また、各世代にどれだけのアクティブなオブジェクトがあるかによって決まります。
ガベージ コレクションはかなり高速に実行できますが、それでもアプリのパフォーマンスに影響を与える可能性があります。一般に、ガベージ コレクション イベントがいつ発生するかをコード内から制御することはできません。 システムは、ガベージ コレクションをいつ実行するかを決定するための実行中の基準セットを持っています。 基準が満たされると、システムはプロセスの実行を停止し、ガベージコレクションを開始します。 アニメーションのような集中的な処理ループの途中や音楽再生中にガベージコレクションが発生すると、処理時間が増加する可能性があります。
さらに、コード フローでは、ガベージ コレクション イベントをより頻繁に発生させたり、通常より長く持続させたりするような作業を行うことがあります。 たとえば、アルファ ブレンディング アニメーションの各フレームで、for ループの最も内側の部分で複数のオブジェクトを割り当てた場合、多くのオブジェクトでメモリ ヒープを汚染する可能性があります。
ガベージ コレクションに関するより一般的な情報については、ガベージ コレクションを参照してください。
- 各アプリ プロセスは、Zygote と呼ばれる既存のプロセスからフォークされます。 Zygote プロセスは、システムが起動したときに開始し、共通のフレームワーク コードおよびリソース (アクティビティ テーマなど) をロードします。 新しいアプリ プロセスを開始するには、システムが Zygote プロセスをフォークして、新しいプロセスでアプリのコードをロードして実行します。 このアプローチにより、フレームワークのコードとリソースに割り当てられたRAMページのほとんどを、すべてのアプリのプロセスで共有することができます。
- ほとんどの静的データは、プロセスに mmap されます。 この手法により、データをプロセス間で共有することができ、また、必要なときにページアウトすることができます。 静的データの例としては、以下が挙げられます。 Dalvik コード (直接 mmapping できるようにあらかじめリンクされた
.odex
ファイルに配置)、アプリ リソース (mmapping できる構造になるようにリソース テーブルを設計し、APK の zip エントリを配置)、および.so
ファイル内のネイティブ コードなどの従来のプロジェクト エレメントです。 - 多くの場所で、Android は明示的に割り当てられた共有メモリ領域 (ashmem または gralloc) を使用して、プロセス間で同じダイナミック RAM を共有します。 たとえば、ウィンドウ サーフェスはアプリと画面合成ソフトの間で共有メモリを使用し、カーソル バッファはコンテンツ プロバイダーとクライアントの間で共有メモリを使用します。
共有メモリが広範囲に使用されているため、アプリが使用しているメモリの量を判断するには、注意が必要です。
アプリのメモリ使用を適切に判断するテクニックは、「RAM 使用量の調査」で説明しています。
アプリのメモリの割り当てと再利用
Dalvik ヒープは、各アプリ プロセスに対して 1 つの仮想メモリ範囲に制約されます。
ヒープの論理サイズは、ヒープによって使用される物理メモリの量と同じではありません。 この値は、他のプロセスと共有されるダーティ ページとクリーン ページの両方を考慮しますが、その RAM を共有するアプリの数に比例した量だけです。 この(PSS)合計が、システムで物理メモリフットプリントとみなされます。
Dalvik ヒープは、ヒープの論理サイズをコンパクトにしません。 Android は、ヒープの末端に未使用の領域がある場合にのみ、論理的なヒープ サイズを縮小することができます。 しかし、ヒープが使用する物理メモリを削減することは可能です。 ガベージコレクションの後、Dalvikはヒープを歩き、未使用のページを見つけ、madviseを使ってそれらのページをカーネルに返します。 そのため、大きなチャンクのペアでの割り当てと解放は、使用される物理メモリのすべて(またはほぼすべて)を再要求する結果になるはずです。 しかし、小さな割り当てからメモリを再要求することは、はるかに効率が悪くなります。
アプリのメモリを制限する
機能的なマルチタスキング環境を維持するために、Android は各アプリのヒープ サイズに厳しい制限を設定します。 正確なヒープ サイズの制限は、デバイスが全体として利用可能な RAM の量に基づいて、デバイス間で異なります。
場合によっては、現在のデバイスで利用可能なヒープ領域を正確に把握するために、システムに問い合わせることがあります。 この図は、getMemoryClass()
を呼び出してシステムに問い合わせることができます。 このメソッドは、アプリのヒープに利用可能なメガバイト数を示す整数を返します。
アプリの切り替え
ユーザーがアプリを切り替えるとき、Android はフォアグラウンドではないアプリ、つまりユーザーから見えない、または音楽再生などのフォアグラウンド サービスを実行していないものをキャッシュに保持します。 たとえば、ユーザーが最初にアプリを起動すると、そのアプリのプロセスが作成されますが、ユーザーがそのアプリから離れると、そのプロセスは終了しません。 しかし、ユーザーがアプリを離れても、そのプロセスは終了しない。システムは、そのプロセスをキャッシュしておく。
アプリにキャッシュされたプロセスがあり、現在必要としないリソースを保持している場合、ユーザーがアプリを使用していないときでさえ、アプリはシステム全体のパフォーマンスに影響を及ぼします。 システムは、メモリなどのリソースが不足すると、キャッシュ内のプロセスを停止させます。
注意: キャッシュ内のアプリの消費メモリが少ないほど、アプリが強制終了されず、迅速に再開できる可能性が高くなります。
前景で実行されていないプロセスがどのようにキャッシュされるか、また、Android がどのようにプロセスを終了させるかを決定する方法の詳細については、『プロセスとスレッド』のガイドを参照してください。