1. ホーム
  2. java

[解決済み] java.lang.OutOfMemoryErrorをキャッチする?

2022-10-24 06:54:55

質問

ドキュメンテーション について java.lang.Error は言う。

Error は Throwable のサブクラスで、合理的なアプリケーションがキャッチしようとすべきではない深刻な問題を示します。

しかし java.lang.Error のサブクラスである java.lang.Throwable のサブクラスであり、このタイプのThrowableをキャッチすることができます。

この種の例外をキャッチするのは良くないというのは理解できました。私の理解では、もしキャッチすることにした場合、キャッチハンドラは自らメモリを確保してはいけないと思います。そうでなければ OutOfMemoryError が再びスローされます。

そこで質問なのですが

  1. をキャッチする際の現実的なシナリオはありますか? java.lang.OutOfMemoryError をキャッチするのは良いアイデアでしょうか?
  2. をキャッチすることにしたら java.lang.OutOfMemoryError をキャッチすることにした場合、キャッチハンドラがそれ自身でメモリを割り当てないことを確認するにはどうすればよいでしょうか(何かツールやベストプラクティスがあれば教えてください)。

どのように解決するのですか?

をキャッチしたいシナリオはいくつもあります。 OutOfMemoryError をキャッチしたい場合がありますが、私の経験では(WindowsとSolaris JVM上で)、非常にまれにしか OutOfMemoryError はJVMへの死刑宣告です。

をキャッチする正当な理由は1つだけです。 OutOfMemoryError をキャッチする良い理由はただ一つで、それは優雅に終了し、リソースをきれいに解放し、失敗の理由をできる限り記録することです(もしそうすることがまだ可能であるならば)。

一般に OutOfMemoryError は、ヒープの残りのリソースで満たすことができないブロックメモリの割り当てが原因で発生します。

が発生すると Error が投げられたとき、ヒープは失敗した割り当ての前と同じ量の割り当てられたオブジェクトを含み、今、クリーンアップのために必要とされるかもしれないより多くのメモリを解放するために、ランタイムオブジェクトへの参照を削除する時間です。これらのケースでは、続けることが可能かもしれませんが、JVMが修復可能な状態であることを100%確実にすることはできないので、それは間違いなく悪い考えでしょう。

デモでは OutOfMemoryError は、JVMがキャッチブロック内でメモリ不足であることを意味しません。

private static final int MEGABYTE = (1024*1024);
public static void runOutOfMemory() {
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    for (int i=1; i <= 100; i++) {
        try {
            byte[] bytes = new byte[MEGABYTE*500];
        } catch (Exception e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            long maxMemory = heapUsage.getMax() / MEGABYTE;
            long usedMemory = heapUsage.getUsed() / MEGABYTE;
            System.out.println(i+ " : Memory Use :" + usedMemory + "M/" +maxMemory+"M");
        }
    }
}

このコードの出力。

1 : Memory Use :0M/247M
..
..
..
98 : Memory Use :0M/247M
99 : Memory Use :0M/247M
100 : Memory Use :0M/247M

クリティカルなものを実行する場合、私は通常 Error をキャッチし、それを syserr に記録し、選択したロギング フレームワークを使ってそれを記録し、それからリソースを解放し、きれいな方法で終了します。起こりうる最悪の事態は何でしょうか?JVMはとにかく死につつあり(あるいはすでに死につつあり)、そして Error をキャッチすることによって、少なくともクリーンアップのチャンスがあります。

注意点は、クリーンアップが可能な場所でのみ、この種のエラーの捕捉を目標としなければならないことです。空白にしないこと catch(Throwable t) {} のような無意味なことはしないでください。