ここでは、スタックについて簡単に説明します。
プログラムがスレッドを生成すると、OSがそのスレッドに対してスタックと呼ばれるメモリ領域をそのスレッドの終了時まで自動的に割り当てます。スタックは、スレッド上で実行されるメソッドや関数が使用するローカル変数などの一時的なデータを格納するための作業域として使用されます。
スレッド上では多くのメソッドや関数が入れ子状態で呼び出されるので、これらのメソッドや関数が使用する一時的な作業域を管理するために、OSはフレームと呼ばれる区切りで個々の作業域をスタック上に積み上げることで管理しています。(メソッドや関数が呼び出されるごとに、これらが使用する作業域をフレームという区切りでスタック上に積み上げ、フレーム内のデータは呼び出したメソッドや関数からの復帰時に破棄されます。)
このため、あるメソッドを再帰的に無限に呼び出すなどしてメソッドを深く呼び出した場合や、非常に大きなサイズのローカル変数などを使用するメソッドを呼び出した場合などには、スタック域の枯渇により、スタック上にフレームを積み上げることができなくなり、スタックオーバーフローが発生する場合があります。
Javaアプリケーションの場合、通常、スタックオーバーフローが発生すると、java.lang.StackOverflowErrorがスローされます。ただし、Javaプロセス中のネイティブモジュール実行時の処理でスタックオーバーフローが発生した場合には、java.lang.StackOverflowErrorはスローされません。なお、FJVMを使用しているJavaプロセス中のネイティブモジュール実行時にスタックオーバーフローが発生した場合は、“8.6.3.1 スタックオーバーフロー検出時のメッセージ出力機能”によってスタックオーバーフローの発生を検出できる場合があります。
注意
スタック領域の管理
スタック領域の実際の管理はOSが行います。そのため、スタック領域に関する管理方法/動作仕様/大きさについては、JDK/JREを実行する各OSの仕様に依存します。
なお、Javaアプリケーション実行時のスタックオーバーフロー発生をJava VMが検出できる様にするために、スタック領域の一部をJava VMの制御用領域として使用します。そのため、Javaアプリケーションから使用できるスタック領域の大きさは、スタックとして割り当られた領域の大きさよりも若干小さな大きさになります。
注意
クラスファイル実行時のスタック領域の利用
Javaの実行環境であるJava VMが起動された後、Java VMは、実行対象プログラムであるクラスファイルを読み込み、クラスファイルを以下の2つの方法を用いて実行します。
インタプリタによるバイトコードの実行
動的コンパイルにより、バイトコードを機械命令に翻訳してから実行
あるクラスファイル中の同じJavaメソッドを実行する場合であっても、Java VMによる実行方法が異なる場合は、実行時に使用されるスタックの大きさが異なります。
なお、クラスファイルの実行方法については、“8.3 動的コンパイル”を参照してください。
注意
ユーザ空間のメモリ不足によるスレッド生成エラー
スタックは、プロセス内のユーザ空間から割り当てられます。
そして、ユーザ空間のメモリ不足によりスタックなどを割り当てることができなった場合には、スレッド生成処理でエラーが発生します。
Javaプロセスの場合、Javaヒープなどのサイズを大きく確保すると、その分だけスタックとして割当て可能な領域が減少するため、Javaプロセス内で生成可能なスレッドの個数も減少します。
このため、Javaプロセス内のスレッド生成処理がエラーとなる要因の1つとして、Javaヒープなどのサイズを大きく確保したことによるユーザ空間のメモリ不足が考えられます。
注意
Javaプロセスの消滅
Windows(R)では、Javaプロセス内でスタックオーバーフローが発生した場合、システム状態やプログラム状態によっては、OSからFJVMやワトソン博士に制御が渡らず、痕跡を残さずにJavaプロセスが消滅する場合があります。
なお、ワトソン博士の説明は、“8.5.9.1 クラッシュダンプ”を参照してください。