7 シグナルおよび例外の処理
この章の構成は、次のとおりです。
LinuxおよびmacOSでのシグナルの処理
Java HotSpot VMは、様々な機能を実装して致命的エラー状態に対処するために、シグナル・ハンドラをインストールします。
たとえば、まれにjava.lang.NullPointerException
がスローされた場合に明示的なNULLチェックを回避する最適化では、SIGSEGV
シグナルをキャッチして処理し、NullPointerException
をスローします。
一般に、シグナルやトラップが発生する場合には2つのカテゴリがあります。
-
暗黙的なNULL処理のような、シグナルが予想され処理される場合。もう1つの例として、セーフポイントが必要になったときにメモリー内のページを保護する、セーフポイント・ポーリング・メカニズムがあります。そのページにアクセスするスレッドによって
SIGSEGV
が生成され、その結果、スレッドをセーフポイントに移行させるスタブが実行されます。 -
予期しないシグナル。これには、VMコード、Java Native Interface (JNI)コード、またはネイティブ・コードで実行されたときの
SIGSEGV
が含まれます。これらの場合、このシグナルは予想されていないため、エラー・ログを作成してプロセスを終了するために致命的エラー処理が呼び出されます。
表7-2に、LinuxおよびmacOSオペレーティング・システムで現在使用されているシグナルの一覧を示します。
Windowsでの例外処理
Windowsでは、例外はプログラムの実行中に発生するイベントです。
例外には、ハードウェア例外とソフトウェア例外の2種類があります。ハードウェア例外は、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
ハンドラを登録すると、CRTは対応する例外をシグナルに変換します。CRTの起動コードは、main()
関数の周囲に__try
/__exceptブロックを実装しています。CRTの例外フィルタ関数(名前は_XcptFilter
)は、Win32例外をシグナルにマップし、シグナルを適切なハンドラにディスパッチします。シグナルのハンドラをSIG_DFL
(デフォルトの処理)に設定すると、_XcptFilter
はUnhandledExceptionFilter
を呼び出します。
ベクトル化例外処理メカニズムを使用することもできます。ベクトル化ハンドラは、フレームベースのハンドラではありません。プログラムは、AddVectoredExceptionHandler APIを使用して0個以上のベクトル化例外ハンドラを登録できます。ベクトル化ハンドラは、構造化例外ハンドラ(あれば)の前に呼び出され、例外の発生場所に関係なく呼び出されます。
ベクトル化例外ハンドラは、次のいずれかの値を返します。
-
EXCEPTION_CONTINUE_EXECUTION
: 次のベクトル化およびSEHハンドラをスキップします。 -
EXCEPTION_CONTINUE_SEARCH
: 次のベクトル化またはSEHハンドラを続行します。
シグナル・チェーン
シグナル・チェーンでは、独自のシグナル・ハンドラをインストールする必要があるアプリケーションを書けます。この機能はLinuxおよびmacOSで使用できます。
シグナル・チェーン機能は、次の機能を提供します。
-
OracleのHotSpot仮想マシンの作成時に事前インストールされるシグナル・ハンドラに対するサポート。
HotSpot VMが作成されるときに、HotSpot VMで使用されるシグナルのシグナル・ハンドラが保存されます。実行中にこれらのシグナルが生成され、その対象がHotSpot VMでない場合は、事前インストールされたハンドラが呼び出されます。つまり、これらのシグナルのために、事前インストールされたシグナル・ハンドラはHotSpot VMハンドラの背後でチェーンされます。
-
Java Native Interfaceコード内または別のネイティブ・スレッドからのどちらかで、HotSpot VMを作成した後にインストールされたシグナル・ハンドラのサポート。
アプリケーションは、
libc/libthread/libpthread
ライブラリの前にlibjsig.so
共有ライブラリをリンクしてロードできます。HotSpot VMによってすでにインストールされているシグナル・ハンドラとハンドラが競合する場合に、このライブラリでは、signal()
、sigset()
、sigaction()
などの呼出しがインターセプトされて、HotSpot VMで使用されるシグナル・ハンドラが置き換えれません。かわりに、これらのコールでは、新しいシグナル・ハンドラが保存されます。新しいシグナル・ハンドラは、これらのシグナルのために、HotSpot VM シグナル・ハンドラの背後でチェーンされます。実行中にこれらのシグナルが生成され、その対象がHotSpot VMでない場合は、事前インストールされたハンドラが呼び出されます。VMの作成後にインストールされるシグナル・ハンドラに対するサポートが必要ない場合、
libjsig.so
共有ライブラリは不要です。シグナル・チェーンを有効にするには、次の手順のいずれかを実行して
libjsig.so
共有ライブラリを使用します。-
HotSpot VMを作成または埋め込むアプリケーションに
libjsig.so
共有ライブラリをリンクします。cc -L libjvm.so-directory -ljsig -ljvm java_application.c
-
LD_PRELOAD
環境変数を使用します。-
Kornシェル(ksh):
export LD_PRELOAD=libjvm.so-directory/libjsig.so; java_application
-
Cシェル(csh):
setenv LD_PRELOAD libjvm.so-directory/libjsig.so; java_application
-
この間に呼び出される
signal()
、sigset()
、およびsigaction()
コールは、HotSpot VMによってインストールされ、オペレーティング・システムで認識されているシグナル・ハンドラではなく、保存されたシグナル・ハンドラを返します。 -
ノート:
SIGQUIT
、SIGTERM
、SIGINT
、およびSIGHUP
シグナルはチェーンできません。アプリケーションでこれらのシグナルを処理する必要がある場合は、—Xrs
オプションの使用を検討してください。
macOSのシグナル・チェーンの有効化
macOSでシグナル・チェーンを有効にするには、次の環境変数を設定します。
-
DYLD_INSERT_LIBRARIES
: Linuxで使用可能なLD_PRELOAD
環境変数のかわりに、指定されたライブラリがプリロードされます。 -
DYLD_FORCE_FLAT_NAMESPACE
: macOSには(シンボルの完全修飾名にそのライブラリが含まれる)2レベル・ネームスペースがあるため、libjsig
ライブラリの関数を有効にしてOSの実装を置き換えます。この機能を有効にするには、この環境変数を任意の値に設定します。
次のコマンドは、libjsig
ライブラリをプリロードして、シグナル・チェーンを有効にします。
$ DYLD_FORCE_FLAT_NAMESPACE=0 DYLD_INSERT_LIBRARIES="JAVA_HOME/lib/libjsig.dylib" java MySpiffyJavaApp
ノート:
macOSのライブラリ・ファイル名はlibjsig.dylib
で、Linuxの場合のlibjsig.so
ではありません。
Java HotSpot VMによる例外処理
HotSpot VMでは、64ビット・システム用のAddVectoredExceptionHandlerAPIを使用して、初期化中に最上位の例外ハンドラをインストールします。
また、作成される各スレッドのスレッド(内部)起動関数呼出しの周囲に、C++の__try
/__except
ブロックを使用してWin32 SEHをインストールします。
最後に、JNI関数の周囲に例外ハンドラをインストールします。
アプリケーションでJNIコード内の構造化例外を処理する必要がある場合は、C++の__try
/__except
文を使用できます。ただし、JNIコード内でベクトル化例外ハンドラを使用する必要がある場合、そのハンドラはVMの例外ハンドラに進むためにEXCEPTION_CONTINUE_SEARCH
を返す必要があります。
一般に、例外が発生する2つのカテゴリがあります。
-
例外が予想され、処理されるとき。例として、前述の暗黙的なNULL処理があります(以前はNULLへのアクセスには
EXCEPTION_ACCESS_VIOLATION
を使用して処理されていました)。 -
予想外の例外。例として、VMコード、JNIコード、またはネイティブ・コード内で実行されているときの
EXCEPTION_ACCESS_VIOLATION
があります。これらの場合、このシグナルは予想されていないため、エラー・ログを作成してプロセスを終了するために致命的エラー処理が呼び出されます。
コンソール・ハンドラ
このトピックでは、Java HotSpot VMに登録されているコンソール・イベントのリストについて説明します。
Java HotSpot VMは、表7-1に示すコンソール・イベントを登録します。
表7-1 コンソール・イベント
コンソール・イベント | シグナル | 使用方法 |
---|---|---|
|
|
このイベントとシグナルは、プロセスを終了するために使用されます。(省略可能) |
|
|
このイベントとシグナルは、VMが異常終了したときにシャットダウン・フック・メカニズムによって使用されます。(省略可能) |
|
|
このイベントとシグナルは、Javaスタック・トレースを標準エラー・ストリームに出力するために使用されます。(省略可能) |
アプリケーションが独自のコンソール・ハンドラを登録する必要がある場合は、-Xrs
オプションを使用できます。このオプションを使用すると、(前述のイベントのマッピングによる) SIGTERM
に対してシャットダウン・フックが実行されず、(前述の[Ctrl]+[Break]イベントのマッピングによる) SIGBREAK
に対してスレッド・ダンプのサポートを利用できなくなります。
LinuxおよびmacOSで使用されるシグナル
このトピックでは、LinuxおよびmacOSで使用されるシグナルのリストについて説明します
表7-2 LinuxおよびmacOSで使用されるシグナル
シグナル | 説明 |
---|---|
|
これらのシグナルは暗黙的なNULLチェックの実装などに使用されます。 |
|
このシグナルは、Javaスタック・トレースを標準エラー・ストリームに出力するために使用されます。(省略可能) |
|
これらのシグナルは、VMが異常終了したときにシャットダウン・フック・メカニズム( |
|
このシグナルはLinuxおよびmacOSで内部的に使用されます。 |
|
HotSpot VMはこのシグナルを処理しません。かわりに、致命的エラー処理の後で |
シグナルの使用を減らすために-Xrs
オプションを指定すると、「オプション」としてタグ付けされたシグナルは使用されません。このオプションを使用すると、使用されるシグナルの数が減りますが、VMはSIGSEGV
などの基本的なシグナルのために独自のシグナル・ハンドラをインストールします。このオプションを指定すると、プロセスがSIGQUIT
、SIGTERM
、SIGINT
、またはSIGHUP
を受信した場合にシャットダウン・フック・メカニズムが実行されなくなります。VMが正常終了した場合(つまり、最後の非デーモン・スレッドが完了するか、System.exitメソッドが呼び出されると)、シャットダウン・フックが予想どおり実行されます。
SIGUSR2
は、suspendとresumeをLinuxおよびmacOSで実装するために使用されます。ただし、SIGUSR2
のかわりに代替シグナルを使用するように指定することも可能です。これを行うには、_JAVA_SR_SIGNUM
環境変数を指定します。この環境変数を設定する場合は、SIGSEGV
およびSIGBUS
の最大より大きい値に設定する必要があります。