※当サイトの記事には、広告・プロモーションが含まれます。

JavaのOutOfMemoryErrorはhs_err_pid.xxxxx.logファイルを出力しないのがほとんどな気がするんだが...

nazology.net

⇧ amazing...

JavaのOutOfMemoryErrorとは

OracleさんのAPIドキュメントによると、

docs.oracle.com

モリー不足のためにJava Virtual Machineがオブジェクトを割り当てることができず、ガベージ・コレクタによっても使用可能なメモリーをこれ以上確保できない場合にスローされます。まるで抑制が無効になっているか、スタック・トレースへの書込みができないか、あるいはその両方であるかのように、仮想マシンによってOutOfMemoryErrorオブジェクトが構築される可能性があります。

https://docs.oracle.com/javase/jp/8/docs/api/java/lang/OutOfMemoryError.html

⇧ とありますと。

java.lang.Error」クラスの系統に分類されますと。

OutOfMemoryErrorは非検査例外

で、

www.oracle.com

⇧ 上記サイト様の説明によりますと、「java.lang.Error」クラスってのは、「Unchecked Exception(非検査例外)」に該当しますと。

で、

stackoverflow.com

⇧ stackoverflowとかでも、

  • cheked(検査)
  • unchecked(非検査)

で分類してくれているんだけど、Oracleさんのドキュメントだと、

docs.oracle.com

⇧ 特に、「checked(検査)」「unchecked(非検査)」の分類を丁寧に説明してくれてるわけじゃないというね...

まぁ、Oracleさん説明をまとめるのが下手くそ過ぎるだけなのかもしらんけど、

docs.oracle.com

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のドキュメントになるのですが、

docs.oracle.com

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」というファイルを生成するらしい。

で、

docs.oracle.com

⇧ 上記の「致命的エラー・ログ」のドキュメントを見ると、

docs.oracle.com

-XX:ErrorFile=fileフラグを指定しない場合、デフォルトのログ・ファイル名はhs_err_pid.log(pidはプロセスのPID)になります。

https://docs.oracle.com/javase/jp/8/docs/technotes/guides/troubleshoot/felog001.html#BABIDHJC

また、-XX:ErrorFile=fileフラグを指定しない場合、システムはそのファイルをプロセスの作業ディレクトリに作成しようとします。作業ディレクトリにファイルを作成できない場合(領域不足、アクセス権の問題、またはその他の問題がある場合)は、オペレーティング・システムの一時ディレクトリにファイルが作成されます。Oracle SolarisおよびLinuxオペレーティング・システムの一時ディレクトリは/tmpです。Windowsの場合、一時ディレクトリはTMP環境変数の値によって指定されます。その環境変数が定義されていない場合は、TEMP環境変数の値が使用されます。

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ファイル

の関係が分からないのだけど、

docs.oracle.com

Oracleの公式のドキュメントだと、

  1. Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space
    →ログ・ファイル出力の記載なし
  2. Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded
    →ログ・ファイル出力の記載なし
  3. Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
    →ログ・ファイル出力の記載なし
  4. Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace
    →ログ・ファイル出力の記載なし
  5. Exception in thread thread_name: java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?
    →致命的エラー・ログ・ファイルを生成します
  6. Exception in thread thread_name: java.lang.OutOfMemoryError: Compressed class space
    →ログ・ファイル出力の記載なし
  7. 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?」のケースのみなんだが...

qiita.com

また、JVMがクラッシュした時にでるログ、hs_err_.logのpidと、
/var/log/messages配下にkillしたpidが一致していたら、OOM Killerによってkillされたことがわかる。

【JVM】OOM(Out Of Memory)障害対応に挑戦しよう #Java - Qiita

⇧ 上記サイト様によりますと、「OutOfMemoryError」の障害対応で「hs_err_pid.xxxxx.log」ファイルを確認する、って仰られておりますが、Oracleの公式のドキュメントを見た限り、「OutOfMemoryError」でも「hs_err_pid.xxxxx.log」ファイルが生成されないケースの方が多そうなんだが...

OutOfMemoryErrorで「hs_err_pid.xxxxx.log」ファイルが生成されるのか試してみる

とりあえず、

terazzo.hatenadiary.org

⇧ 上記サイト様を参考に試してみます。

■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で捕捉すべきじゃないって言うんなら、全てのエラーのケースでデフォルトでログ・ファイルにエラー内容を出力するようにしておいて欲しいんだが...

毎度モヤモヤ感が半端ない...

今回はこのへんで。