Cause di memoria insufficiente di JVM

Aug 20 2020

Le domande si basano su Oracle Hotspot JDK8.

Quando l'applicazione incontra java.lang.OutOfMemory: Java heap spaceun'eccezione, suppongo, dove sono due possibili ragioni.

  1. La dimensione dell'heap JVM allocato raggiunge la dimensione -Xmxspecificata e il sistema GC non può spremere abbastanza spazio.
  2. L'heap JVM allocato non raggiunge -Xmx, ma la memoria fisica non è sufficiente per la crescita dell'heap JVM. Supponiamo che -Xms< -Xmx.

So che 1è un motivo per cui JVM rinuncia java.lang.OutOfMemory: Java heap spaceall'eccezione. È 2una ragione ragionevole?

Trovo alcuni articoli menzionati java.lang.OutOfMemoryError: native memory exhausted, ma sono tutti limitati al sito Web IBM. Questa scadenza è limitata alla JVM implementata da IBM o è una scadenza standard nella specifica JVM?


Ho fatto alcuni esperimenti con il codice fornito da @Eugene in risposta. Come ha notato @Holger, il risultato varia a seconda degli ambienti. Ho provato sia CentOS x64 che Win7 x64, con Hotspot JDK8 x64. Per semplicità lo swap e la memoria virtuale sono disabilitati.

Passo dopo passo aumento il limite di memoria (-Xmx e -Xms).

I. -Xmx < memoria logica disponibile

  • Sia su CentOS che su Windows mostra OutOfMemoryError: Java heap space

II. memoria logica disponibile <-Xmx < memoria fisica massima

  • CentOS: il GC tenta di completare i tempi server del GC, con errore di allocazione , e il processo viene interrotto dal sistema, lasciando un messaggio Killed .
  • Windows: il GC tenta di completare i tempi server del GC, con errore di allocazione , e lancia OutOfMemoryError: Java heap space

III. -Xmx> memoria fisica massima

  • CentOS: come in II
  • Windows: come in II

IV. -Xms> memoria fisica massima

  • CentOS: JVM sembra non avviarsi. Il messaggio di errore è come:

Avviso VM server Java HotSpot (TM) a 64 bit: INFO: os :: commit_memory (0x00000000e62a0000, 349569024, 0) non riuscito; errore = 'Impossibile allocare memoria' (errno = 12)

  • Windows: JVM non è riuscito ad avviare. Il messaggio di errore è come:

Si è verificato un errore durante l'inizializzazione del VM Impossibile riservare spazio sufficiente per l'heap degli oggetti


Quindi, la stessa JVM si comporta in modo diverso in diversi sistemi operativi.

  • Su Windows, il sistema operativo non uccide JVM. E JVM lancia sempre OutOfMemoryError: lo spazio heap Java quando l'utilizzo della memoria è maggiore.
  • Su Linux, il sistema operativo interrompe i processi quando non c'è abbastanza memoria.
  • Su entrambi i sistemi operativi JVM non è riuscito ad avviarsi quando la memoria disponibile non soddisfa i requisiti minimi di JVM.

Risposte

1 Eugene Aug 20 2020 at 18:04

Prima di tutto ci sono più ragioni per cui un GC fallisce con "memoria esaurita", come spiega il commento sotto la tua domanda.

Dimostrare il numero del punto (2) è facile, basta creare un codice che assegni sempre:

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());
}

Ed eseguilo con -Xms1g -Xmx100g, su un sistema che ha meno 100gRAM. Puoi abilitare i log GC (l'ho fatto con i "-Xlog:heap*=debug" "-Xlog:gc*=debug"flag java-9 per esempio) e vedere quanto GC sta cercando di far fronte a questa allocazione costante, fallendo alla fine.