⇧ amazing...
JavaのOutOfMemoryErrorとは
メモリー不足のためにJava Virtual Machineがオブジェクトを割り当てることができず、ガベージ・コレクタによっても使用可能なメモリーをこれ以上確保できない場合にスローされます。まるで抑制が無効になっているか、スタック・トレースへの書込みができないか、あるいはその両方であるかのように、仮想マシンによってOutOfMemoryError
オブジェクトが構築される可能性があります。
https://docs.oracle.com/javase/jp/8/docs/api/java/lang/OutOfMemoryError.html
⇧ とありますと。
「java.lang.Error」クラスの系統に分類されますと。
OutOfMemoryErrorは非検査例外
で、
⇧ 上記サイト様の説明によりますと、「java.lang.Error」クラスってのは、「Unchecked Exception(非検査例外)」に該当しますと。
で、
⇧ stackoverflowとかでも、
- cheked(検査)
- unchecked(非検査)
で分類してくれているんだけど、Oracleさんのドキュメントだと、
⇧ 特に、「checked(検査)」「unchecked(非検査)」の分類を丁寧に説明してくれてるわけじゃないというね...
まぁ、Oracleさん説明をまとめるのが下手くそ過ぎるだけなのかもしらんけど、
Here's the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.
https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html
⇧「unchecked(非検査)」は、回復不能な、つまりリカバリーができないような例外のことらしく、「OutOfMemoryError」は、「unchecked(非検査)」に該当するようなので、try catchとかするべきじゃない、ってことらしい。
致命的なエラーって何?
Java 8のドキュメントが見当たらず、Java 11のドキュメントになるのですが、
10 致命的エラー・レポート
致命的なエラーとは、ネイティブ・メモリーの枯渇、メモリー・アクセス・エラー、またはプロセスに向けられた明示的なシグナルなどのエラーのことです。致命的なエラーは、アプリケーション内のネイティブ・コード(たとえば、開発者によって記述されたJava Native Interface (JNI)コードなど)、アプリケーションやJVMで使用するサードパーティのネイティブ・ライブラリ、またはJVM内のネイティブ・コードによってトリガーされます。致命的なエラーが原因でJVMをホストしているプロセスが終了すると、JVMはエラーに関する情報を収集し、クラッシュ・レポートを書込みます。
https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html
JVMは、エラーの性質と場所を特定しようと試みます。可能な場合、JVMはクラッシュ時のJVMとプロセスの状態に関する詳細な情報を書込みます。入手できる詳細は、プラットフォームおよびクラッシュの性質によって異なります。このエラー・レポート・メカニズムよって提供される情報を使用すると、アプリケーションのデバッグやサードパーティ・コードの問題の特定をより簡単に、効率よく行うことができます。エラー・メッセージからJVMコードの問題がわかれば、より正確で有用なバグ・レポートを提出できます。場合によっては、クラッシュ・レポートの生成によって、詳細なレポート生成を阻害する二次的なエラーが発生することがあります。
https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html
エラー・レポートの例
次の例は、アプリケーションのネイティブJNIコードのクラッシュに関するエラー・レポート(hs_err_pid18240.log
ファイル)の冒頭を示しています。
https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html
⇧ とあって、「致命的なエラー」は、「hs_err_pid.xxxxx.log」というファイルを生成するらしい。
で、
⇧ 上記の「致命的エラー・ログ」のドキュメントを見ると、
-XX:ErrorFile=file
フラグを指定しない場合、デフォルトのログ・ファイル名はhs_err_pid.log(pidはプロセスのPID)になります。
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/felog001.html#BABIDHJC
⇧ とあるように、JVMのオプションが絡んでるらしい。
JavaのOutOfMemoryErrorはhs_err_pid.xxxxx.logファイルを出力しないのがほとんどな気がするんだが...
で、いまいち、
- OutOfMemoryError
- 致命的なエラー
- hs_err_pid.xxxxx.logファイル
の関係が分からないのだけど、
⇧ Oracleの公式のドキュメントだと、
- Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space
→ログ・ファイル出力の記載なし - Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded
→ログ・ファイル出力の記載なし - Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
→ログ・ファイル出力の記載なし - Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace
→ログ・ファイル出力の記載なし - Exception in thread thread_name: java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?
→致命的エラー・ログ・ファイルを生成します - Exception in thread thread_name: java.lang.OutOfMemoryError: Compressed class space
→ログ・ファイル出力の記載なし - Exception in thread thread_name: java.lang.OutOfMemoryError: reason stack_trace_with_native_method
→ログ・ファイル出力の記載なし
となっていて、「致命的エラー・ログ・ファイル」が生成されると言っているのは、「5.Exception in thread thread_name: java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?」のケースのみなんだが...
また、JVMがクラッシュした時にでるログ、hs_err_.logのpidと、
/var/log/messages配下にkillしたpidが一致していたら、OOM Killerによってkillされたことがわかる。
⇧ 上記サイト様によりますと、「OutOfMemoryError」の障害対応で「hs_err_pid.xxxxx.log」ファイルを確認する、って仰られておりますが、Oracleの公式のドキュメントを見た限り、「OutOfMemoryError」でも「hs_err_pid.xxxxx.log」ファイルが生成されないケースの方が多そうなんだが...
OutOfMemoryErrorで「hs_err_pid.xxxxx.log」ファイルが生成されるのか試してみる
とりあえず、
⇧ 上記サイト様を参考に試してみます。
■1.Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space
import java.util.ArrayList; import java.util.List; /** * case 01 * Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space */ public class OOMESample01 { public static void main(String[] args) { // クラッシュするコード List<byte[]> buffers = new ArrayList<byte[]>(); while (true) { buffers.add(new byte[1024 * 1024 * 1024]); } } }
「hs_err_pid.xxxxx.log」ファイル出力されませんな...
続いて、以下のエラーのケース。
■2.Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded
import java.util.ArrayList; import java.util.List; /** * case 02 * Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded */ public class OOMESample02 { public static void main(String[] args) { List<String> stringList = new ArrayList<>(); List<List<String>> list = new ArrayList<>(); while (true) { List<String> newList = new ArrayList<>(); for (int i = 0; i < 10000; i++) { newList.add(new String("This is an example of a GC Overhead Error")); } list.add(newList); } } }
「hs_err_pid.xxxxx.log」ファイル出力されませんな...
続いて、以下のエラーのケース。
■3.Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
/** * case 03 * Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit */ public class OOMESample03 { public static void main(String[] args) { // クラッシュするコード byte[] buffer = new byte[Integer.MAX_VALUE]; } }
「hs_err_pid.xxxxx.log」ファイル出力されませんな...
続いて、以下のエラーのケース。
■4.Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace
import net.sf.cglib.proxy.Enhancer; /** * case 04 * Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace */ public class OOMESample04 { public static void main(String[] args) { for (int i = 0; i < 1000; i++) { new Thread(() -> { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyClass.class); enhancer.setUseCache(false); enhancer.setCallback(new MyMethodInterceptor()); enhancer.create(); } }).start(); } } static class MyClass { public void myMethod() { } } static class MyMethodInterceptor implements net.sf.cglib.proxy.MethodInterceptor { public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, net.sf.cglib.proxy.MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } } }
「hs_err_pid.xxxxx.log」ファイル出力されませんな...
続いて、以下のエラーのケースと思ったけど、Javaのソースコードで意図的発生させるのが無理っぽい。というか唯一の「hs_err_pid.xxxxx.log」ファイル出力するケースが検証できんではないか...
■5.Exception in thread thread_name: java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?
テストの実施方法が分からず
続いて、以下のエラーのケースと思ったけど、Javaのソースコードで意図的発生させるのが無理っぽい。
■6.Exception in thread thread_name: java.lang.OutOfMemoryError: Compressed class space
テストの実施方法が分からず
続いて、以下のエラーのケースと思ったけど、Javaのソースコードで意図的発生させるのが無理っぽい。
■7.Exception in thread thread_name: java.lang.OutOfMemoryError: reason stack_trace_with_native_method
テストの実施方法が分からず
という感じで、OutOfMemoryErrorが起きたかどうかは、シェルに出力されるログを確認する感じなんかね?
OutOfMemoryErrorは、try catchで捕捉すべきじゃないって言うんなら、全てのエラーのケースでデフォルトでログ・ファイルにエラー内容を出力するようにしておいて欲しいんだが...
毎度モヤモヤ感が半端ない...
今回はこのへんで。