ここでは、スタックについて簡単に説明します。
プログラムがスレッドを生成すると、OSがそのスレッドに対してスタックと呼ばれるメモリ領域をそのスレッドの終了時まで自動的に割り当てます。スタックは、スレッド上で実行されるメソッドや関数が使用するローカル変数などの一時的なデータを格納するための作業域として使用されます。
スレッド上では多くのメソッドや関数が入れ子状態で呼び出されるので、これらのメソッドや関数が使用する一時的な作業域を管理するために、OSはフレームと呼ばれる区切りで個々の作業域をスタック上に積み上げることで管理しています。(メソッドや関数が呼び出されるごとに、これらが使用する作業域をフレームという区切りでスタック上に積み上げ、フレーム内のデータは呼び出したメソッドや関数からの復帰時に破棄されます。)
このため、あるメソッドを再帰的に無限に呼び出すなどしてメソッドを深く呼び出した場合や、非常に大きなサイズのローカル変数などを使用するメソッドを呼び出した場合などには、スタック域の枯渇により、スタック上にフレームを積み上げることができなくなり、スタックオーバーフローが発生する場合があります。
Javaアプリケーションの場合、通常、スタックオーバーフローが発生すると、java.lang.StackOverflowErrorがスローされます。ただし、Javaプロセス中のネイティブモジュール実行時の処理でスタックオーバーフローが発生した場合には、java.lang.StackOverflowErrorはスローされません。なお、FJVMを使用しているJavaプロセス中のネイティブモジュール実行時にスタックオーバーフローが発生した場合は、“スタックオーバーフロー検出時のメッセージ出力機能”によってスタックオーバーフローの発生を検出できる場合があります。
スタックは、プロセス内のユーザ空間から割り当てられます。
そして、ユーザ空間のメモリ不足によりスタックなどを割り当てることができなった場合には、スレッド生成処理でエラーが発生します。
Javaプロセスの場合、Javaヒープなどのサイズを大きく確保すると、その分だけスタックとして割当て可能な領域が減少するため、Javaプロセス内で生成可能なスレッドの個数も減少します。
このため、Javaプロセス内のスレッド生成処理がエラーとなる要因の1つとして、Javaヒープなどのサイズを大きく確保したことによるユーザ空間のメモリ不足が考えられます。
Windows(R)では、Javaプロセス内でスタックオーバーフローが発生した場合、システム状態やプログラム状態によっては、OSからFJVMやワトソン博士に制御が渡らず、痕跡を残さずにJavaプロセスが消滅する場合があります。
なお、ワトソン博士の説明は、“7.3.4 クラッシュダンプ・コアダンプ”を参照してください。
Windows Server(R) for Itanium-based Systemsでは、スタックとして以下の2種類の領域を使用します。
メモリ用スタック域
レジスタ用スタック域
そのためOSは、プログラムがスレッド生成時に指定したスタックサイズの値を用いて、その両方のスタック域を新たに生成するスレッドへ割り当てます。
つまりスレッドに対しては、スタックサイズとして指定された値の2倍の大きさがスタックとして割り当てられます。
たとえば、スタックサイズを2MBとしてスレッドを起動した場合には、そのスレッドに対するスタック域として、両スタック域の合計である4MBが割り当てられます。