Android上のアプリケーションはJava言語で開発できるが、PCやサーバー向けのJava仮想マシンとは異なる「Dalvik仮想マシン」上で動作する。Dalvik仮想マシンは、モバイル向けに特化されているのが特徴である。
Androidアプリケーションの開発では、Javaの開発現場で培ってきた資産やノウハウを生かすことができる。だが、基本的にPC上で動作させるJavaアプリケーションと同様に考えてしまうと、思わぬ落とし穴に陥ることになる。
そこで、主にメモリー管理の面からJava仮想マシンとDalvik仮想マシンの違いを見ていくことにする。
両者とも、メモリー管理はガベージコレクタ(GC)が担当する。ヒープ領域に空きがない場合、不必要なオブジェクトを回収して空き領域を作るのがGCの役割である。Java仮想マシンでは「世代別GC」という方式を採用しているが、Dalvik仮想マシンは「マークアンドスイープ」という方式を採用している。
世代別GCでは、ヒープを大きく「New世代領域」と「Old世代領域」の二つに分ける。New世代領域では、生成直後のオブジェクトが順次保存され、生成後、回収されずに生き残った長命のオブジェクトはOld世代領域にコピーしていく。
この方式は、「大多数のオブジェクトは短命」という仮説に基づいたものであり、大量の短命なオブジェクトを回収するのに最適化されている。ヒープを世代という概念で分割したことにより、一度にすべての世代を再配置する必要がない。結果として、パフォーマンスに影響を与えるGCの起動を最小限にとどめることができる。
これに対しDalvik仮想マシンでは、まず確実に必要なオブジェクトに印を付け、そのオブジェクトから参照をたどり、使っているオブジェクトに印を付けていく。この作業を繰り返して、印の付いていないオブジェクトは不必要なオブジェクトとみなし、破棄する。
Androidはマークアンドスイープ方式のGCを実行中、すべてのスレッドを停止する。その間、動作がもたつくような感覚を、ユーザーに与えてしまうことになる。
Java仮想マシンのGCは短命なオブジェクトの回収に最適化されているが、AndroidのGCはそのような処理には適していない。Androidでは、なるべく短命なオブジェクトの作成は避けるべきである。
Android SDKには「DDMS(Dalvik Debug Monitor Service)」(画面1)と呼ばれるデバッグ用のツール群が付属しており、そのツールの一つ「Allocation Tracker」では、メモリーの確保状況を追跡できる。どのスレッドのどのタイミングでメモリーが割り当てられたのかが一目瞭然なので、活用するといいだろう。
Javaの「常識」は、必ずしも通用するとは限らない。
アシアル株式会社 エンジニア