Cause di memoria insufficiente di JVM
Le domande si basano su Oracle Hotspot JDK8.
Quando l'applicazione incontra java.lang.OutOfMemory: Java heap space
un'eccezione, suppongo, dove sono due possibili ragioni.
- La dimensione dell'heap JVM allocato raggiunge la dimensione
-Xmx
specificata e il sistema GC non può spremere abbastanza spazio. - 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 space
all'eccezione. È 2
una 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
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 100g
RAM. 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.