開発者は、Java アプリケーションをシグナルハンドラまたは例外ハンドラを使用するコードと統合しなければいけない場合があります。この章では、HotSpot 仮想マシンでのシグナルの処理方法について説明します。また、独自のシグナルハンドラをインストールする必要があるアプリケーションの記述を容易にする、シグナルチェーン機能についても説明します。シグナルチェーン機能は、Solaris OS および Linux 上で使用できます。
HotSpot 仮想マシンは、さまざまな機能を実装して致命的エラー状態を処理するために、シグナルハンドラをインストールします。たとえば、まれに java.lang.NullPointerException がスローされた場合に明示的な NULL チェックを回避する最適化では、SIGSEGV シグナルをキャッチして処理し、NullPointerException をスローします。
一般に、シグナルやトラップが発生する状況には 2 つのカテゴリがあります。
シグナルが予想され、処理される状況。例として、上記の暗黙的な NULL 処理があります。もう 1 つの例として、セーフポイントが必要になったときにメモリー内のページを保護する、セーフポイントポーリングメカニズムがあります。そのページにアクセスするスレッドによって SIGSEGV が生成され、その結果、スレッドをセーフポイントに移行させるスタブが実行されます。
予想外のシグナル。これには、VM コード、JNI コード、またはネイティブコードで実行されたときの SIGSEGV が含まれます。これらの場合、このシグナルは予想されていないため、エラーログを作成してプロセスを終了するために致命的エラー処理が呼び出されます。
次の表に、Solaris OS および Linux 上で現在使用されているシグナルを示します。「オプション」という記述は、「6.1.1 シグナル使用の削減」で説明するように、-Xrs オプションを指定したときにそのシグナルが不要になることを意味します。「構成可能」という記述は、
「6.1.2 代替シグナル」で説明するように、代替シグナルを指定できることを意味します。シグナルチェーンの詳細は、
「6.1.3 シグナルチェーン」を参照してください。
|
-Xrs オプションは、シグナルの使用を減らすように HotSpot VM に指示します。このオプションを使用すると、使用されるシグナルの数が減りますが、VM は SIGSEGV などの基本的なシグナルのために独自のシグナルハンドラをインストールします。-Xrs を指定すると、上の表で「オプション」と記述されたシグナルは使用されません。このオプションを指定すると、プロセスが SIGQUIT、SIGTERM、SIGINT、または SIGHUP を受信した場合にシャットダウンフックメカニズムが実行されなくなります。VM が正常終了した (最後の非デーモンスレッドが完了するか、System.exit メソッドが使用された) 場合は、シャットダウンフックが予想どおり実行されます。
Solaris 8 および 9 OS では、-XX:+UseAltSigs オプションを使用して、SIGUSR1 および SIGUSR2 の代替シグナルを使用するように HotSpot VM に指示できます。Solaris 10 OS 以降では、オペレーティングシステムが (SIGJVM1 および SIGJVM2 という名前の) 2 つの追加シグナルを予約しているため、このオプションは無視されます。
Linux では、SIGUSR1 のハンドラをオーバーライドすることはできません。SIGUSR2 は、suspend と resume を実装するために使用されます。ただし、SIGUSR2 の代わりに代替シグナルを使用するように指定することも可能です。これを行うには、_JAVA_SR_SIGNUM 環境変数を指定します。この環境変数を設定する場合は、SIGSEGV および SIGBUS の最大より大きい値に設定する必要があります。
ネイティブコードを含むアプリケーションに独自のシグナルハンドラが必要な場合は、シグナルチェーン機能とともにアプリケーションを使用しなければいけないことがあります。シグナルチェーン機能は、次の機能を提供します。
HotSpot VM の作成時に事前インストールされるシグナルハンドラに対するサポート。
VM が最初に作成されるときに、既存のシグナルハンドラ (つまり、VM で使用されるシグナルのハンドラ) が保存されます。実行中にこれらのシグナルが生成され、その対象が Java HotSpot VM でない場合は、事前インストールされたハンドラが呼び出されます。つまり、これらのシグナルのために、事前インストールされたハンドラは VM ハンドラの背後でチェーンされます。
HotSpot VM 作成後に JNI コード内部または別のネイティブスレッドからインストールされるシグナルハンドラに対するサポート。
アプリケーションは、libc/libthread/libpthread の前に libjsig.so 共有ライブラリをリンクしてロードできます。このライブラリは、それらのハンドラが Java HotSpot VM によってすでにインストールされたものと競合する場合に、signal()、sigset()、sigaction() などの呼び出しを遮断してそれらが Java HotSpot VM のシグナルハンドラを実際に置き換えないようにします。代わりに、これらの呼び出しは、新しいシグナルハンドラを保存するか、または VM がインストールしたハンドラの背後にそれらをチェーンします。実行中にこれらのシグナルが生成され、その対象が Java HotSpot VM でない場合は、事前インストールされたハンドラが呼び出されます。
VM の作成後にインストールされるシグナルハンドラに対するサポートが必要ない場合、libjsig.so 共有ライブラリは不要です。
libjsig.so 共有ライブラリを使用するには、これら 2 つの手順のいずれかを実行します。
次のように、HotSpot VM を作成する/埋め込むアプリケーションにリンクします。
cc -L libjvm.so-directory -ljsig -ljvm java_application.c
次のように、LD_PRELOAD 環境変数を使用します。
export LD_PRELOAD=libjvm.so-directory/libjsig.so; java_application(ksh)
setenv LD_PRELOAD libjvm.so-directory/libjsig.so; java_application(csh)
この間に呼び出される signal()、sigset()、および sigaction() は、Java HotSpot VM によってインストールされ、オペレーティングシステムで認識されているシグナルハンドラではなく、保存されたシグナルハンドラを返します。
SIGUSR1 はチェーンできません。Solaris OS 上でアプリケーションがこのシグナルをチェーンしようとすると、HotSpot VM は次の致命的エラーで終了します。
Signal chaining detected for VM interrupt signal, try -XX:+UseAltSigs
さらに、SIGQUIT、SIGTERM、SIGINT、および SIGHUP シグナルもチェーンできません。アプリケーションでこれらのシグナルを処理する必要がある場合は、-Xrs オプションの使用を検討してください。
Solaris OS では、SIGUSR2 シグナルをチェーンできますが、Java 以外かつ VM 以外のスレッド専用です。つまり、VM に接続していないアプリケーションで作成されたネイティブスレッドでのみ使用できます。
Windows では、例外はプログラムの実行中に発生するイベントです。例外には、ハードウェア例外とソフトウェア例外の 2 種類があります。ハードウェア例外は、Solaris OS および Linux 上の SIGSEGV や SIGKILL などのシグナルに相当します。ソフトウェア例外は、アプリケーションまたはオペレーティングシステムが RaiseException() API を使用して明示的に発行します。
Windows では、ハードウェア例外とソフトウェア例外の両方を処理するメカニズムを構造化例外処理 (SEH) と呼びます。これは、C++ や Java の例外処理メカニズムに似たスタックフレームベースの例外処理です。C++ では、次の例のように __try および __except キーワードを使用して、例外が発生する可能性があるコードのセクションを保護します。
__try { // guarded body of code } __except (filter-expression) { // exception-handler block }
__except ブロックは、例外コード (GetExceptionCode() API から返される整数コード)、例外情報 (GetExceptionInformation() API)、またはその両方を使用するフィルタ式によってフィルタ処理されます。
このフィルタ式は、次のいずれかの値として評価されるはずです。
EXCEPTION_CONTINUE_EXECUTION = -1
このフィルタ式によって状況が修復され、例外が発生した場所から実行が継続されます。一部の例外スキームと異なり、SEH は再開モデルもサポートします。これは、シグナルハンドラの完了後にプログラムが中断した場所から実行が継続するという点で、Unix のシグナル処理とよく似ています。違いは、この場合のハンドラがフィルタ式だけであり、__except ブロックではないことです。ただし、フィルタ式に関数呼び出しを含めることもできます。
EXCEPTION_CONTINUE_SEARCH = 0
現在のハンドラはこの例外を処理できません。次のハンドラのハンドラ検索が続行されます。これは、C++ および Java で例外型を照合しない catch ブロックに似ています。
EXCEPTION_EXECUTE_HANDLER = 1
現在のハンドラはこの例外を照合し、処理できます。__except ブロックが実行されます。
終了ハンドラを構築するには、以下に示すように __try および __finally キーワードを使用します。
__try { // guarded body of code } __finally { // __finally block }
(例外のあと、または例外なしで) __try ブロックに制御が残ったときは、__finally ブロックが実行されます。__finally ブロックの内部で AbnormalTermination() API を呼び出すと、例外のあとで制御が継続したかどうかをテストできます。
Windows プログラムは、最上位の未処理例外フィルタ関数をインストールして、__try/__except ブロックで処理されない例外をキャッチすることもできます。この関数は、SetUnhandledExceptionFilter() API を使用してプロセス単位でインストールされます。ある例外のハンドラが存在しない場合は、UnhandledExceptionFilter() が呼び出され、これが最上位の未処理例外フィルタ関数を (あれば) 呼び出して、その例外をキャッチします。この関数によって、未処理例外をユーザーに通知するメッセージボックスも表示されます。
Windows の例外は、現在の実行ストリームに起因する Unix の同期シグナルに相当します。Windows では、コンソールイベント (たとえば、ユーザーがコンソールで Ctrl + C を押した場合) などの非同期イベントは、SetConsoleCtlHandler() API を使用して登録されたコンソール制御ハンドラによって処理されます。
Windows 上でアプリケーションが signal() API を使用すると、C 実行時ライブラリ (CRT) は Windows の例外とコンソールイベントの両方を適切なシグナルまたは C 実行時エラーにマップします。たとえば、CRT は Ctrl + C を SIGINT に、ほかのすべてのコンソールイベントを SIGBREAK にマップします。同様に、SIGSEGV ハンドラを登録すると、C 実行時ライブラリは対応する例外をシグナルに変換します。CRT ライブラリの起動コードは、main() 関数の周囲に __try/__except ブロックを実装しています。CRT の例外フィルタ関数 (名前は _XcptFilter) は、Win32 例外をシグナルにマップし、シグナルを適切なハンドラにディスパッチします。シグナルのハンドラを SIG_DFL (デフォルトの処理) に設定すると、_XcptFilter は UnhandledExceptionFilter を呼び出します。
Windows XP または Windows 2003 では、ベクトル化例外処理メカニズムを使用することもできます。ベクトル化ハンドラは、フレームベースのハンドラではありません。プログラムは、AddVectoredExceptionHandler API を使用して 0 個以上のベクトル化例外ハンドラを登録できます。ベクトル化ハンドラは、構造化例外ハンドラ (あれば) の前に呼び出され、例外の発生場所に関係なく呼び出されます。
ベクトル化例外ハンドラは、次のいずれかの値を返します。
EXCEPTION_CONTINUE_EXECUTION: 次のベクトル化および SEH ハンドラをスキップします。
EXCEPTION_CONTINUE_SEARCH: 次のベクトル化または SEH ハンドラを続行します。
Windows の例外処理の詳細は、Microsoft の Web サイト (http://www.microsoft.com) を参照してください。
HotSpot VM は、VM の初期化中に SetUnhandledExceptionFilter API (64 ビットの場合は AddVectoredExceptionHandler API) を使用して最上位の例外ハンドラをインストールします。
また、作成される各スレッドのスレッド (内部) 起動関数呼び出しの周囲に、C++ の __try /__except ブロックを使用して win32 SEH をインストールします。
最後に、JNI 関数の周囲に例外ハンドラをインストールします。
アプリケーションで JNI コード内の構造化例外を処理する必要がある場合は、C++ の __try /__except 文を使用できます。ただし、JNI コード内でベクトル化例外ハンドラを使用する必要がある場合、そのハンドラは VM の例外ハンドラに進むために EXCEPTION_CONTINUE_SEARCH を返す必要があります。
一般に、例外が発生する状況には 2 つのカテゴリがあります。
シグナルが予想され、処理される状況。例として、前述の (NULL へのアクセスによって EXCEPTION_ACCESS_VIOLATION が発生し、処理される) 暗黙的な NULL 処理があります。
予想外の例外。例として、VM コード、JNI コード、またはネイティブコード内で実行されているときの EXCEPTION_ACCESS_VIOLATION があります。これらの場合、このシグナルは予想されていないため、エラーログを作成してプロセスを終了するために致命的エラー処理が呼び出されます。
HotSpot 仮想マシンは、次の表に示すコンソールイベントを登録します。
|
アプリケーションが独自のコンソールハンドラを登録する必要がある場合は、-Xrs オプションを使用できます。このオプションを使用すると、(上記のイベントのマッピングによる) SIGTERM に対してシャットダウンフックが実行されず、(上記の Ctrl + Break イベントのマッピングによる) SIGBREAK に対してスレッドダンプのサポートを利用できなくなります。