JVM のメモリ構造

Revised: 2nd/Nov./2003; Since: 26th/Jan./2003

データ・エリア

JVM のメモリ構造は、スタックとヒープに大別されます。ヒープ (Heap) は GC の対象で、JVM 起動時に割り当てられる広大な領域です。Java 仮想マシン・スタック (Java Virtual MAchine Stack) はスレッドごとに割り当てられる、メソッド起動ごとにフレーム (Frame) と呼ばれるデータを出し入れする線形のデータ構造です。クラスのインスタンスなどはヒープに格納しますが、インスタンスのような GC 対象となる動的なデータと、クラス構造などの静的なデータは、別の領域に保持し、静的な構造を保持する領域をメソッド・エリア (Method Area) と呼びます。

JVM のメモリ構造
図:JVM のメモリ構造

Java 仮想マシン・スタック

JVM はプロセスの一つとして、OS から CPU 時間をディスパッチされます。プロセス内部では、スレッドという単位でアプリケーションにリソースを割り当てます。アプリケーションは、最低でも一つのスレッドを持ち、言語使用でマルチ・スレッドをサポートしていることは、Java の特徴の一つになります。

スレッドが起動すると、JVM は、自分のプロセスのメモリ構造内に Java 仮想マシン・スタック (Java Virtual Machine Stack) を割り当てます。一般に、Java 仮想マシン・スタックは固定長の線形データ構造で、後入れ先出し (LIFO: Last-In-First-Out) の待ち行列(Stack)を実装し、データの出し入れをプッシュとポップと呼びます。

スタックのデータ

スタック上のデータの単位をフレームと呼びます。当該メソッドのローカル変数の配列や、処理対象のデータを保持するスタック (Operand Stack)、当該メソッドのクラスの実行時コンスタント・プールへの参照を保持します。

メソッドが起動されると、スタック上にフレームと呼ばれる単位のデータが積み上げられ(プッシュされ)、呼び出されたメソッドがアクティブになります。当該メソッドの処理が終了すると、当該フレームは削除され(ポップされ)、呼び出し元のメソッドが再びアクティブになります。実行中のメソッドとそのフレームを、カレント・メソッド、カレント・フレームと呼びます。データ構造上では、Java 仮想マシン・スタックの先頭のフレームがカレント・フレームになって、対応するメソッドがアクティブになります。

スタックの特徴

フレームはメソッド終了時にポップされて破棄され、スタック自身もスレッド終了と共にメモリ上からドロップされる、寿命の短いデータ・エリアです。

固定長の線形データ構造であるスタックに対する、フレームのプッシュ/ポップによる管理は、簡潔で高速です。ローカル変数のプリミティブ型の値や、オブジェクトへの参照などのデータが含まれているので、Java仮想マシン・スタック上のフレームをトレースすることで、アプリケーションの動作の概略を解析することが可能です。

ヒープ

ヒープ (Heap) は、JVM 毎に一つ割り当てられ、全てのスレッドから共有されます。一般に、ヒープは広大な領域を確保し、実行されるアプリケーションの必要に応じて拡大します。

ヒープのデータ

オブジェクトのインスタンス、メソッド・エリアなどの、アプリケーション実行時に必要になるデータを保持します。ヒープの一部であるメソッド・エリア (method area) は、JVM 起動時に割り当てられ、実行時コンスタント・プール、フィールドやメソッドのデータなどのクラスの構造が格納されます。実行時コンスタント・プール (Runtime Constant Pool) は、クラス/インタフェース毎に生成され、リテラルやメソッド/フィールドへの参照が格納されます。

ヒープの特徴

ヒープは、全てのスレッドのスタックから共有され、JVM が起動している限り持続する寿命の長いデータ・エリアです。

スタックのように固定サイズの直線的なデータ構造ではないために、複雑なデータ管理が必要となり、ヒープ領域にあるデータに対するアクセスは低速になります。アプリケーションで動的に割り当てを行うために、領域の断片化(フラグメンテーション)が発生しやすく、メモリ・リーク/領域違反が発生するのもこの領域です。Java では、ヒープ上のデータ・エリアの管理は、GC (Garbage Collector)が受け持っており、アプリケーションが明示的に解放することはできません。

データエリアのサイズ

これらのデータ領域間の参照の繰り返しによってアプリケーションは動作し、多くのJVM実装では、必要があればデータ・エリアは拡張していきます。実行中にメモリ領域を確保できない場合は、対応する例外が投げられます。代表的な例外は、次の二つです。

ここで挙げたデータ・エリアのサイズは、起動時にオプションで指定可能になっています。指定できるオプションは、実装によって異なりますが、SunのJVMでは次のオプションが指定可能です。

他のオプションのサマリーを、コマンド"java -X"でリストすることができます。詳細は、"Java HotSpot VM Options" を参照してください。また、ベンダー、バージョンごとに、指定可能なオプションの種類とデフォルト値が異なっているので、各社のドキュメントを参照する必要があります。

サブルーチンとメソッド

オブジェクト指向とプロシージャ志向の区別をハイライトしてみます。

プロシージャ指向のサブルーチンの場合は、サブルーチンがヒープに展開され、その参照がスタックに積み上げられます。サブルーチンが呼び出されると、サブルーチンコールはスタックに積み上げられて、コンピュータ制御がヒープ上のサブルーチンのコードに移ります。コンピュータ制御がサブルーチンから抜けると、先ほど積み上げられたサブルーチンコールはスタックから取り除かれ、呼び出し元のコードに制御が戻ります。

オブジェクトのメソッドの場合も、サブルーチンと同様に処理されます。その最大の違いは、データの取り扱いです。サブルーチンの場合、一時的にしか存在しないサブルーチンコールに引数として渡すため、メモリ上に持続的に展開されたコードにはデータは保持されていません。一方、オブジェクトの場合、ヒープに持続的に展開されているコード内にデータが埋め込まれており、メソッドは自身が属するオブジェクトのデータだけを処理できます。

サブルーチンもオブジェクトもメモリ上に持続的に展開されています。サブルーチンはデータを持たない処理であり、オブジェクトはデータと処理が一つになったものです。この違いの意味するところが、今は漠然としたものに感じるかもしれませんが、アプリケーション開発の分析と設計を決定的に異なるものとします。

アプリケーション・サーバ

Apache Tomcat や IBM Webspehre Application Server (WAS) のような、サーブレットや JSP のようなサーバサイドの Java 実行環境を、アプリケーション・サーバと呼びます。アプリケーション・サーバは、多くの場合、 JVM 上で実行される Pure Java アプリケーションとして実装されています。

詳細は、製品ごとに異なりますが、管理サーバがアプリケーションサーバをサブ・プロセスとして起動します。管理サーバも、アプリサーバも、JVM として起動し、各々の機能は JVM 上のアプリとして実装されます。

例えば、アプリサーバは、HTTP サーバの機能を実装しており、Embedded HTTP Server と呼ばれていますが、これらは JVM 上のスレッドとして起動する Pure Java Application として実装されているものです。



Copyright © 2003 SUGAI, Manabu. All Rights Reserved.
SEO [PR] !uO z[y[WJ Cu