JVMのメモリ不足の原因

Aug 20 2020

質問は、Oracle HotspotJDK8に基づいています。

アプリケーションがjava.lang.OutOfMemory: Java heap space例外に遭遇したとき、2つの考えられる理由があると思います。

  1. 割り当てられたJVMヒープサイズが-Xmx指定されたサイズに達し、GCシステムが十分なスペースを絞り出すことができません。
  2. 割り当てられたJVMヒープは到達しませんが-Xmx、JVMヒープを拡張するのに十分な物理メモリがありません。-Xms<と仮定し-Xmxます。

1JVMがjava.lang.OutOfMemory: Java heap space例外をスローする理由はわかっています。ある2合理的な原因の一つは?

言及されている記事をいくつか見つけましたjava.lang.OutOfMemoryError: native memory exhaustedが、それらはすべてIBMWebサイトに限定されています。このエクスペディションはIBMが実装したJVMに限定されていますか、それともJVM仕様の標準エクスペディションですか?


@Eugeneから提供されたコードを使っていくつかの実験を行いました。@Holgerが指摘したように、結果は環境によって異なります。私は、CentOSx64とWin7x64の両方で、Hotspot JDK8x64を使用してテストしました。簡単にするために、スワップと仮想メモリは無効になっています。

メモリバウンド(-Xmxおよび-Xms)を段階的に増やします。

I. -Xmx <使用可能なロジックメモリ

  • CentOSとWindowsの両方で、OutOfMemoryError:Javaヒープスペースが表示されます

II。使用可能なロジックメモリ<-Xmx <最大物理メモリ

  • CentOS:GCは、割り当ての失敗を使用して、GCのサーバー時間をフルに試行し、プロセスがシステムによって強制終了され、メッセージKilledが残ります。
  • Windows:GCは、割り当ての失敗でGCサーバー時間をフルにしようとし、OutOfMemoryError:Javaヒープスペースをスローします。

III。-Xmx>最大物理メモリ

  • CentOS:IIと同じ
  • Windows:IIと同じ

IV。-Xms>最大物理メモリ

  • CentOS:JVMが起動しないようです。エラーメッセージは次のようなものです。

Java HotSpot(TM)64ビットサーバーVM警告:情報:os :: commit_memory(0x00000000e62a0000、349569024、0)が失敗しました。error = 'メモリを割り当てることができません'(errno = 12)

  • Windows:JVMを起動できませんでした。エラーメッセージは次のようなものです。

VMの初期化中にエラーが発生しましたオブジェクトヒープ用に十分なスペースを予約できませんでした


そのため、同じJVMはOSによって動作が異なります。

  • Windowsでは、OSはJVMを強制終了しません。また、JVMは、メモリ使用量が増加すると、常にOutOfMemoryError:Javaヒープスペースをスローします
  • Linuxでは、十分なメモリがない場合、OSはプロセスを強制終了します。
  • 両方のOSで、使用可能なメモリがJVMの最小要件を満たしていない場合、JVMを起動できませんでした。

回答

1 Eugene Aug 20 2020 at 18:04

まず第一に、あなたの質問の下のコメントが説明しているように、GCが「メモリ不足」で失敗する理由は他にもあります。

ポイント番号(2)の証明は簡単で、常に割り当てるコードを作成するだけです。

public static void main(String[] args) {
    test(1);
}

static void test(int x){
    List<byte[]> list = new ArrayList<>();
    while(x == 1){
        byte [] b =new byte[1 * 1024 * 1024];
        b[100] = 42;
        list.add(b);
    }

    System.out.println(list.hashCode());
}

そして、RAM-Xms1g -Xmx100gが少ないシステムで、を使用してこれを実行し100gます。GCログを有効にして("-Xlog:heap*=debug" "-Xlog:gc*=debug"たとえば、java-9フラグで行った)、GCがこの一定の割り当てに対処しようとして、最終的に失敗することを確認できます。