システム・クラッシュをトラブルシューティングする具体的ないくつかの手順に関する情報と指針。
クラッシュ(致命的エラー)が発生すると、プロセスが異常終了します。クラッシュにはさまざまな理由が考えられます。たとえば、Java HotSpot VM内、システム・ライブラリ内、Java SEのライブラリまたはAPI内、アプリケーション・ネイティブ・コード内、さらにはオペレーティング・システム(OS)内のバグによってクラッシュが発生する可能性があります。OSのリソース不足などの外部要因によってクラッシュが発生することもあります。
Java HotSpot VMまたはJava SEライブラリ・コードのバグによってクラッシュが発生することはほとんどありません。この章では、クラッシュを調査し、(可能な場合は)バグの原因が診断され修正されるまで問題のいくつかを回避する方法を提案します。
一般にどのようなクラッシュでも、最初の手順は致命的エラー・ログを見つけることです。これは、クラッシュの発生時にJava HotSpot VMによって生成されるテキスト・ファイルです。このファイルを見つける方法とファイルの詳細な説明については、「致命的エラー・ログ」を参照してください。
この章の構成は、次のとおりです。
エラー・ログを使用してクラッシュの原因を見つける方法を説明するいくつかの例、およびその原因に応じて問題をトラブルシューティングするためのいくつかのヒント。
エラー・ログのヘッダーは、エラーのタイプと問題のあるフレームを示し、スレッド・スタックは現在のスレッドおよびスタック・トレースを示しています。「ヘッダー形式」を参照してください。
クラッシュには次の原因が考えられます。
クラッシュ・ダンプ・ファイルまたはコア・ファイルを分析して、ネイティブ・コードまたはJava Native Interface (JNI)ライブラリ・コードでクラッシュが発生したかどうかを識別します。
問題のあるフレームがネイティブ・ライブラリであることが致命的エラー・ログで示されている場合は、ネイティブ・コードまたはJava Native Interface (JNI)ライブラリ・コード内にバグが存在する可能性があります。クラッシュが他のなにかで発生した可能性もありますが、最初はライブラリとコア・ファイルまたはクラッシュ・ダンプを分析するのが適切です。致命的なエラー・ログのヘッダーからの次の例の抜粋を検討してください。
# An unexpected error has been detected by HotSpot Virtual Machine: # # SIGSEGV (0xb) at pc=0x417789d7, pid=21139, tid=1024 # # Java VM: Java HotSpot(TM) Server VM (6-beta2-b63 mixed mode) # Problematic frame: # C [libApplication.so+0x9d7]
この場合は、ライブラリlibApplication.so
内で実行されているスレッドでSIGSEGV
が発生しました。
場合によっては、ネイティブ・ライブラリ内のバグがJava VMコード内のクラッシュとして表れます。_thread_in_vm
状態で(つまり、Java VMコードで実行されているとき)、JavaThread
が失敗した次の例のクラッシュを検討してください。
# An unexpected error has been detected by HotSpot Virtual Machine: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x08083d77, pid=3700, tid=2896 # # Java VM: Java HotSpot(TM) Client VM (1.5-internal mixed mode) # Problematic frame: # V [jvm.dll+0x83d77] --------------- T H R E A D --------------- Current thread (0x00036960): JavaThread "main" [_thread_in_vm, id=2896] : Stack: [0x00040000,0x00080000), sp=0x0007f9f8, free space=254k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) V [jvm.dll+0x83d77] C [App.dll+0x1047] <========= C/native frame j Test.foo()V+0 j Test.main([Ljava/lang/String;)V+0 v ~StubRoutines::call_stub V [jvm.dll+0x80f13] V [jvm.dll+0xd3842] V [jvm.dll+0x80de4] V [jvm.dll+0x87cd2] C [java.exe+0x14c0] C [java.exe+0x64cd] C [kernel32.dll+0x214c7] :
この場合、問題のあるフレームはVMフレームですが、スレッド・スタックは、App.dll
内のネイティブ・ルーチンが(おそらくJNIを使って)VM内に呼び出されたことを示しています。
ネイティブ・ライブラリでのクラッシュを解決する最初の手順は、クラッシュが発生したネイティブ・ライブラリのソースを調べることです。
ネイティブ・ライブラリがアプリケーションによって提供されている場合は、ネイティブ・ライブラリのソース・コードを調べます。-Xcheck:jni
オプションをコマンド行に追加してアプリケーションを実行することで、JNIコードに関する多くの問題を特定できます。「-Xcheck:jniオプション」を参照してください。
ネイティブ・ライブラリが別のベンダーから提供され、アプリケーションで使用されている場合は、このサードパーティ・アプリケーションに対するバグ・レポートを提出し、致命的エラー・ログの情報を提供します。
クラッシュが発生したネイティブ・ライブラリがJava Runtime Environment (JRE)の一部(awt.dllやnet.dllなど)である場合は、ライブラリまたはAPIのバグが発生した可能性があります。その場合は、できるだけ多くのデータを収集し、ライブラリ名を示してバグまたはレポートを提出してください。JREライブラリは、JREディストリビューションのjre/lib
またはjre/bin
ディレクトリにあります。「バグ・レポートの提出」を参照してください。
ネイティブ・アプリケーション・ライブラリ内のクラッシュをトラブルシューティングするには、ネイティブ・デバッガをコア・ファイルまたはクラッシュ・ダンプ(利用可能な場合)に接続します。ネイティブ・デバッガは、OSに応じてdbx
、gdb
、またはwindbg
です。「オペレーティング・システムのネイティブ・ツール」を参照してください。
致命的エラー・ログを分析して、コンパイル済コードでクラッシュが発生したかどうかを識別します。
コンパイル済コードでクラッシュが発生したことが致命的エラー・ログで示されている場合は、不正なコードを生成するコンパイラ・バグが発生した可能性があります。問題のあるフレームのタイプがJ
(コンパイル済みのJavaフレームを意味する)である場合は、コンパイル済みコードでクラッシュが発生したと認識できます。次に示すのはこのようなクラッシュの例です。
# An unexpected error has been detected by HotSpot Virtual Machine: # # SIGSEGV (0xb) at pc=0x0000002a99eb0c10, pid=6106, tid=278546 # # Java VM: Java HotSpot(TM) 64-Bit Server VM (1.6.0-beta-b51 mixed mode) # Problematic frame: # J org.foobar.Scanner.body()V # : Stack: [0x0000002aea560000,0x0000002aea660000), sp=0x0000002aea65ddf0, free space=1015k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) J org.foobar.Scanner.body()V [error occurred during error reporting, step 120, id 0xb]
注意:
完全なスレッド・スタックは入手できません。「error occurred during error reporting」(エラー報告中にエラーが発生しました)という出力行は、スタック・トレースを取得しようとして問題が発生したこと(スタックの破損を示している可能性がある)を意味します。コンパイラを切り替えるか、クラッシュを引き起こしたメソッドをコンパイルから除外することによって、問題を一時的に回避できる可能性があります。
致命的エラー・ログを分析して、HotSpotコンパイラ・スレッドでクラッシュが発生したかどうかを識別します。
致命的エラー・ログの出力に、現在のスレッドがCompilerThread0
、CompilerThread1
、またはAdapterCompiler
という名前のJavaThread
であることが示されている場合は、コンパイラのバグが発生した可能性があります。この場合は、コンパイラを切り替える(たとえば、HotSpot Server VMのかわりにHotSpot Client VMを使用する、またはその逆)か、クラッシュを引き起こしたメソッドをコンパイルから除外することによって、問題を一時的に回避しなければいけないことがあります。
致命的エラー・ログを分析して、VMThread
でクラッシュが発生したかどうかを識別します。
致命的エラー・ログの出力に、現在のスレッドがVMThread
であることが示されている場合は、THREAD
セクションでVM_Operation
を含む行を探してください。VMThread
は、HotSpot VMの特別なスレッドです。VM内でガベージ・コレクション(GC)などの特殊なタスクを実行します。VM_Operation
によって、操作がGCであることが示唆された場合は、ヒープの破損などの問題が発生した可能性があります。
GCの問題以外にも、ヒープ内のオブジェクト参照を不整合または不正な状態にしておく他の何か(コンパイラやランタイムのバグなど)が問題である可能性もあります。この場合は、環境に関する情報をできるだけ多く収集し、可能な回避策を試してください。問題がGCに関連する場合は、GCの構成を変更することによって問題を一時的に回避できることがあります。
「ガベージ・コレクション中のクラッシュの回避」を参照してください。
Java言語コード内でスタック・オーバーフローが発生すると、通常は違反したスレッドがjava.lang.StackOverflowError
例外をスローします。
一方、CおよびC++はスタックの終わりを超えて書込みを行い、スタック・オーバーフローを起こします。これは、プロセスが終了する致命的エラーです。
HotSpot実装では、Javaメソッドがスタック・フレームをC/C++ネイティブ・コード(つまり、ユーザー・ネイティブ・コードおよび仮想マシン自体)と共有します。Javaメソッドは、スタック空間を超えることなくネイティブ・コードを呼び出せるように、スタックの終わりに向かって一定距離のスタック空間が使用可能であるかどうかをチェックするコードを生成します。スタックの終わりまでの距離をシャドウ・ページと呼びます。シャドウ・ページのサイズは、プラットフォームに応じて3-20ページです。この距離は調整可能であるため、デフォルトより長い距離を必要とするネイティブ・コードを含むアプリケーションは、シャドウ・ページのサイズを増やすことができます。シャドウ・ページを増やすためのオプションは-XX:StackShadowPages=
nですnはプラットフォームのデフォルトのシャドウ・ページより大きくします。
アプリケーションでセグメンテーション・フォルトが発生してもコア・ファイルや致命的エラー・ログがない場合は、「致命的エラー・ログ」を参照してください。または、Windows上でSTACK_OVERFLOW_ERROR
が発生したか、または「An irrecoverable stack overflow has occurred」(回復不能なスタック・オーバーフローが発生しました)というメッセージが表示された場合、これはStackShadowPages
の値を超過したため、より多くの空間が必要であることを示しています。
StackShadowPages
の値を増やす場合は、-Xss
パラメータを使用してデフォルトのスレッド・スタック・サイズも増やす必要があることがあります。デフォルトのスレッド・スタック・サイズを増やすと、作成できるスレッドの数が減る可能性があるため、スレッド・スタック・サイズの値は慎重に選択してください。スレッド・スタック・サイズは、プラットフォームによって256 KBから1024 KBまで様々です。
# An unexpected error has been detected by HotSpot Virtual Machine: # # EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x10001011, pid=296, tid=2940 # # Java VM: Java HotSpot(TM) Client VM (1.6-internal mixed mode, sharing) # Problematic frame: # C [App.dll+0x1011] # --------------- T H R E A D --------------- Current thread (0x000367c0): JavaThread "main" [_thread_in_native, id=2940] : Stack: [0x00040000,0x00080000), sp=0x00041000, free space=4k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) C [App.dll+0x1011] C [App.dll+0x1020] C [App.dll+0x1020] : C [App.dll+0x1020] C [App.dll+0x1020] ...<more frames>... Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) j Test.foo()V+0 j Test.main([Ljava/lang/String;)V+0 v ~StubRoutines::call_stub
前述の例から次の情報を解釈できます。
例外はEXCEPTION_STACK_OVERFLOW
です。
スレッドの状態は_thread_in_native
で、スレッドがネイティブ・コードまたはJNIコードを実行していることを意味します。
スタック情報では、空き領域は4 KB (Windowsシステムの単一ページ)のみです。さらに、スタック・ポインタ(sp
)はスタックの終わり(0x00040000
)に近い0x00041000
です。
ネイティブ・フレームの出力は、再帰的なネイティブ関数がこの場合の問題であることを示しています。出力の...
という表記は、追加のフレームが存在するが、出力されなかったことを示します。出力は100フレームまでに制限されています。
重要なアプリケーションでクラッシュが発生した場合に使用可能な回避策。
重要なアプリケーションでクラッシュが発生し、HotSpot VM内のバグによってクラッシュが発生したように見える場合は、一時的な回避策をすばやく見つけることが望ましい場合があります。JDKの最新リリースとともに配備されたアプリケーションでクラッシュが発生した場合は、Oracleにクラッシュを報告してください。
重要:
このセクションの回避方法によってクラッシュが正常に除去されても、その回避方法は問題の修正ではなく、一時的な解決方法でしかありません。問題が見つかった元の構成で、サポートに電話するか、バグ・レポートを提出してください。
システム・クラッシュの回避方法を見つけるための3つのシナリオを次に示します。
HotSpotコンパイラ・スレッドでクラッシュが発生した場合に使用可能な回避策。
コンパイラ・スレッドでクラッシュが発生したことが致命的エラー・ログに示された場合は、コンパイラのバグが発生した可能性があります(ただし、常にそうであるとはかぎりません)。同様に、コンパイルされたコードでクラッシュが発生した場合は、コンパイラが不正なコードを生成した可能性があります。
HotSpot Client VM (-client
オプション)の場合は、コンパイラ・スレッドがCompilerThread0
としてエラー・ログに表示されます。HotSpot Server VMの場合は、複数のコンパイラ・スレッドがあり、それらがCompilerThread0
、CompilerThread1
、およびAdapterThread
としてエラー・ログ・ファイルに表示されます。
JDK 7u5のリリース以降、HotSpotコンパイラはデフォルトで無視されます。コマンドライン・オプションを使用して以前の動作をシミュレートすることが可能であり、これは複数のメソッドが除外されている場合に便利です。JDK 7u5の重要なバグ修正に関する項を参照してください。
.hotspot_compile
ファイルのかわりにJVMフラグを使用してメソッドをコンパイルから除外するには、Java Platform, Standard Editionツール・リファレンスの拡張JITコンパイラ・オプションの-XX:CompileCommand
に関する項を参照してください。
次の例は、開発中に発生し、修正されたコンパイラ・バグのエラー・ログのフラグメントを示しています。このログ・ファイルは、HotSpot Server VMが使用され、CompilerThread1
でクラッシュが発生したことを示しています。このログ・ファイルはさらに、現在のCompileTask
がjava.lang.Thread.setPriority
メソッドのコンパイルだったことを示しています。
# An unexpected error has been detected by HotSpot Virtual Machine: # : # Java VM: Java HotSpot(TM) Server VM (1.5-internal-debug mixed mode) : --------------- T H R E A D --------------- Current thread (0x001e9350): JavaThread "CompilerThread1" daemon [_thread_in_vm, id=20] Stack: [0xb2500000,0xb2580000), sp=0xb257e500, free space=505k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) V [libjvm.so+0xc3b13c] : Current CompileTask: opto: 11 java.lang.Thread.setPriority(I)V (53 bytes) --------------- P R O C E S S --------------- Java Threads: ( => current thread ) 0x00229930 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=21] =>0x001e9350 JavaThread "CompilerThread1" daemon [_thread_in_vm, id=20] :
この場合は、可能な回避策が2つあります。
強引な方法: HotSpot Client VMを指定するため、アプリケーションが-client
オプションで実行されるように構成を変更します。
丁寧な方法: java.lang.Thread.setPriority
メソッドのコンパイル中にのみバグが発生するものと想定して、このメソッドをコンパイルから除外します。
最初の(-client
オプションを使用する)方法は、一部の環境では容易に構成できる可能性があります。それ以外で、構成が複雑な場合や、VMを構成するコマンド行に容易にアクセスできない場合は、より困難になる可能性があります。一般に、HotSpot Server VMからHotSpot Client VMに切り替えることで、アプリケーションのピーク・パフォーマンスも低下します。環境によっては、問題の診断と修正が完了するまで、これを容認できる場合があります。
2つ目の(メソッドをコンパイルから除外する)方法では、アプリケーションの作業ディレクトリ内に.hotspot_compiler
ファイルを作成する必要があります。次の例に、このアプローチを示します。
exclude java/lang/Thread setPriority
一般に、このファイルの形式はexcludeclassmethod
です。ここで、class
は(パッケージ名で完全修飾された)クラス、method
はメソッドの名前です。コンストラクタ・メソッドは<init>
として指定され、staticイニシャライザは<clinit>
として指定されます。
注意:
.hotspot_compiler
ファイルは、サポートされていないインタフェースです。ここでは、トラブルシューティングと一時的な回避策の特定のみを目的として記載しています。
アプリケーションが再起動された後、コンパイラは.hotspot_compiler
ファイル内の除外されたメソッドをコンパイルしようとしません。場合によっては、クラッシュの根本原因を診断してバグを修正するまで、これが一時的な救済策になる可能性があります。
HotSpot VMが、2番目のアプローチの前述の例に示した.hotspot_compiler
ファイルを正しく検出して処理したことを確認するには、実行時に次のログ情報を探してください。
注意:
ファイル名のセパレータはスラッシュではなくドットです。ガベージ・コレクション時にクラッシュが発生した場合に使用可能な回避策。
ガベージ・コレクション(GC)中にクラッシュが発生した場合は、致命的エラー・ログに、VM_Operation
が進行中であることが報告されます。ここでは説明のため、ほぼ同時に処理されるGC (-XX:+UseConcMarkSweep
)は使用されないことを前提としてください。ログのTHREAD
セクションにはVM_Operation
が表示され、次のいずれかの状況を示します。
割当て用の世代コレクション
全世代のコレクション
パラレルGCの割当ての失敗
パラレルGCの永続的な割当ての失敗
パラレルGCのシステムGC
ほとんどの場合、ログで報告される現在のスレッドはVMThread
です。これは、HotSpot VMで特殊なタスクを実行するために使用される特別なスレッドです。次の例は、シリアル・ガベージ・コレクタでのクラッシュからの致命的エラー・ログのフラグメントです。
--------------- T H R E A D --------------- Current thread (0x002cb720): VMThread [id=3252] siginfo: ExceptionCode=0xc0000005, reading address 0x00000000 Registers: EAX=0x0000000a, EBX=0x00000001, ECX=0x00289530, EDX=0x00000000 ESP=0x02aefc2c, EBP=0x02aefc44, ESI=0x00289530, EDI=0x00289530 EIP=0x0806d17a, EFLAGS=0x00010246 Top of Stack: (sp=0x02aefc2c) 0x02aefc2c: 00289530 081641e8 00000001 0806e4b8 0x02aefc3c: 00000001 00000000 02aefc9c 0806e4c5 0x02aefc4c: 081641e8 081641c8 00000001 00289530 0x02aefc5c: 00000000 00000000 00000001 00000001 0x02aefc6c: 00000000 00000000 00000000 08072a9e 0x02aefc7c: 00000000 00000000 00000000 00035378 0x02aefc8c: 00035378 00280d88 00280d88 147fee00 0x02aefc9c: 02aefce8 0806e0f5 00000001 00289530 Instructions: (pc=0x0806d17a) 0x0806d16a: 15 08 83 3d c0 be 15 08 05 53 56 57 8b f1 75 0f 0x0806d17a: 0f be 05 00 00 00 00 83 c0 05 a3 c0 be 15 08 8b Stack: [0x02ab0000,0x02af0000), sp=0x02aefc2c, free space=255k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) V [jvm.dll+0x6d17a] V [jvm.dll+0x6e4c5] V [jvm.dll+0x6e0f5] V [jvm.dll+0x71771] V [jvm.dll+0xfd1d3] V [jvm.dll+0x6cd99] V [jvm.dll+0x504bf] V [jvm.dll+0x6cf4b] V [jvm.dll+0x1175d5] V [jvm.dll+0x1170a0] V [jvm.dll+0x11728f] V [jvm.dll+0x116fd5] C [MSVCRT.dll+0x27fb8] C [kernel32.dll+0x1d33b] VM_Operation (0x0373f71c): generation collection for allocation, mode: safepoint, requested by thread 0x02db7108
注意:
ガベージ・コレクション中のクラッシュがガベージ・コレクション実装内のバグを示すとはかぎりません。コンパイラやランタイムのバグ、またはその他の問題を示す場合もあります。
ガベージ・コレクション中にクラッシュが繰返し発生する場合は、次の回避策を試すことができます。
GCの構成を切り替えます。たとえば、シリアル・コレクタを使用している場合はスループット・コレクタを試します(またはその逆)。
HotSpot Server VMを使用している場合は、HotSpot Client VMを試します。
使用されているガベージ・コレクタがわからない場合は、Oracle SolarisおよびLinuxオペレーティング・システム上でjmap
ユーティリティを使用できます。コア・ファイルが使用可能な場合は、コア・ファイルからヒープ情報を取得するために「jmapユーティリティ」を参照してください。一般に、GC構成がコマンド行で指定されていない場合、Windowsではシリアル・コレクタが使用されます。Oracle SolarisおよびLinuxオペレーティング・システムではマシンの構成に依存します。マシンに少なくとも2 GBのメモリーと少なくとも2基のCPUが搭載されている場合は、スループット・コレクタ(パラレルGC)が使用されます。小規模なマシンでは、シリアル・コレクタがデフォルトです。シリアル・コレクタを選択するためのオプションは-XX:+UseSerialGC
で、スループット・コレクタを選択するためのオプションは-XX:+UseParallelGC
です。回避策としてスループット・コレクタからシリアル・コレクタに切り替えた場合、マルチプロセッサ・システムではパフォーマンスがある程度低下する可能性があります。根本的な問題の診断と修正が完了するまで、これを容認できる場合があります。
JREをインストールすると、インストーラがシステムJARファイルから一連のクラスをprivate内部表現にロードして、その表現を「共有アーカイブ」と呼ばれるファイルにダンプします。JVMを開始すると、共有アーカイブがメモリー・マッピングされ、これらのクラス用の読取り専用のJVMメタデータを複数のJVM処理で共有できます。共有アーカイブの復元はクラスのロードより速いため、起動時間が短縮され、コストを節約できます。クラス・データ共有は、Java HotSpot VMでサポートされます。G1、シリアル、パラレルおよびparallelOldGCガベージ・コレクタがサポートされています。共有文字列機能(クラス・データ共有の一部)では、Windows以外のプラットフォーム上のG1ガベージ・コレクタのみがサポートされています。
致命的エラー・ログでは、ログのヘッダーにバージョン文字列が出力されます。共有が有効になっている場合は、次の例に示すように「sharing」というテキストでそれが示されます。
# An unexpected error has been detected by HotSpot Virtual Machine: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x08083d77, pid=3572, tid=784 # # Java VM: Java HotSpot(TM) Client VM (1.5-internal mixed mode, sharing) # Problematic frame: # V [jvm.dll+0x83d77]
CDSを無効にするには、コマンド行に-Xshare:off
オプションを指定します。共有が有効になっているときにのみクラッシュが発生する場合は、この機能にバグが発生した可能性があります。その場合は、できるだけ多くの情報を収集し、バグ・レポートを提出してください。
JDKソフトウェアは、Microsoft Visual Studio 2013を使用してWindows上に構築されています。
Javaアプリケーションでクラッシュが発生し、コンパイラの異なるリリースでコンパイルされたネイティブ・ライブラリまたはJNIライブラリがある場合は、ランタイム間の互換性の問題を考慮する必要があります。具体的には、Microsoftのガイドラインに従って複数のランタイムを処理している場合にのみ、環境がサポートされます。たとえば、あるランタイムを使用してメモリーを割り当てた場合は、同じランタイムを使用してそれを解放する必要があります。リソースの割当て時と異なるライブラリを使用してリソースを解放すると、予測できない動作やクラッシュが発生する可能性があります。