HPのようにサン・マイクロシステムズからソースコードライセンスを受けている場合、プロセッサやOSに依存する部分の実装や、各種パラメータの設定は独自に行っていますが、それ以外の基本部分はサン・マイクロシステムズのコードをそのまま使っています。VMと同一のものということです。VMと基本部分のソースコードは同一です。VMは特定のOSとハードウェア上に作られた仮想マシンであり、ソフトウェアにより実装されています。VMはJavaの実行環境を提供するソフトウェアエミュレータです。ほかのソフトウェアエミュレータの例としては、Itaniumマシン上のHP-UXでPA-RISC/HP-UXの実行環境を提供するAriesがあります。PA-RISC/HP-UXのアプリケーションをItanium/HP-UXシステム上で実行できるのは、Ariesのおかげです。VMというプログラムが動作しながら、Javaプログラムが同じプロセス内で動作していることになります。VMの仮想メモリ上でのイメージは図5のようになります。VMスタックが必要になります。Javaヒープにはクラスファイルを展開している領域とJavaクラスのインスタンスが置かれる領域があります。VM自身もプログラムなので実行テキストに加えてCヒープとスタックを持ちます。命令ポインタもバイトコードの命令を指すものとネイティブコードを指すものが存在します。Javaプログラムを実行しているスレッドでは、プロセッサの命令ポインタは主にインタプリタ内かコードキャッシュ内を指しています。VMのプロセスメモリ空間(概略)図5の構造を理解しておけばJavaにおける問題発生時の対応に役立ちます。「メモリ不足」という状況になった場合、Cヒープが足りない、Javaプログラムのヒープが足りない、スタックが足りない、OSのスワップが足りないなどさまざまな原因が考えられます。VMがOSのリソースをどのように使用しているかなどを理解しておく必要があります。VMのクラスローダによりファイルからメモリへとロードされます。VMでは、「-verbose:class」を付けて起動するとロードされたクラスを表示してくれます。Generation領域に配置されます。宣言されているフィールドの値などが含まれます。VMの起動時以外でもクラスをロードすることができます。Generation領域の使用量は動的に増えます。Generation領域に置かれます。Javaヒープはどちらの領域もガベージコレクションの対象になります。Javaヒープ内部の詳細は次回のガベージコレクションの回で説明します。VMスタックとはJavaに限らず、一般に関数が1つ呼ばれるたびにスタック上にスタックフレームが1つ積まれます。このスタックフレームの中にはローカル変数と次に呼び出す関数に渡す引数の一部が置かれます。Javaにもスタックがあり、Javaバイトコードインタプリタが利用します。VMスタックのスタックフレーム内には、ローカル変数と引数に加えてオペランドスタックが置かれます。VMがスタックマシンであるために必要なものです。下記のJavaバイトコードはオペランドスタックを利用した演算の例です。これは、0番目と1番目のローカル変数を、(このフレーム内の)オペランドスタック領域に積み、この2つの値を加算します。VMスタックとネイティブコードのスタックは同じ領域に連続して存在します。Javaプログラムのフレームとネイティブコードのフレームが1つのスタック内に混在することになります。リスト1はJavaプログラムを実行しているJavaスレッドのスタックトレースをHPのgdbで表示させたものです。フレーム#14から#0に向かって順番に関数が呼び出されていることを示しています。スレッド生成後にいくつかの処理を行い、途中からJavaアプリケーションを実行しているのが分かります。VM自身のフレームで、#7から#0がJavaプログラムのフレームです。frameと表示されており、main()メソッドがインタプリタで実行していることが分かります。#5のi2c_adapterは、インタプリタで実行されている関数からコンパイルされている関数を呼び出す際に呼ばれるアダプタ関数で、表示されてはいませんが、c2i_adapterはコンパイルされた関数からインタプリタで実行される関数を呼び出すアダプタ関数です。これらアダプタは主に呼び出す関数の引数の順番を変更します。#6も#5と同様、#4を呼び出すためのスタブ的な役割をします。frameと表示されており、JavaのMyLoopクラスのmethod1()メソッドとmethod2()メソッドのコンパイルされたネイティブコードが実行されています。2つのメソッドが1つのフレームに表示されているのはmethod2()がmethod1()にインライン展開されていることを示しています。#3も同様にコンパイルされたメソッドのフレームです。#2はネイティブメソッドを呼び出すためのスタブで、#1と#0ではネイティブメソッドを実行しています。VMの下で動作するOSのスレッドに対応します(native_thread)。UNIXであれば、Javaプログラム実行中にSIGQUITを送って、Windowsであればターミナル上でctrl-breakキーによりスレッドのダンプを表示させることができます。スタックはスレッドごとに存在します。これらのスレッドは2つのグループに分けることができます。1つはJavaスレッドで、もう1つはJavaシステムスレッドです(図8参照)。JavaスレッドはJavaのアプリケーションを実行しているスレッドですが、JavaシステムスレッドはJavaの実行環境を提供します。インタプリタで実行した関数のスタックフレームのみは、ネイティブコード用に決められているコーリングコンベンション(関数呼び出し規則)と異なるフレームフォーマットになります。リスト1で示したJavaスレッドのスタックトレース例は、図8のstack1に当たります。Javaスレッドのスタックのサイズは、-Xssオプションで指定することができます。JavaスレッドとJavaシステムスレッドのスタックJavaシステムスレッドには、ThreadHelper、VMThread、ReferenceHandler、Finalizer、WatcherThread、SuspendCheckerThread、SignalDispatcher、CompilerThreadなどがあります。この中でもReferenceHandlerとFinalizerはJavaで実装されており、図8のstack3に当たります。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。