2013-06-19 10 views
13

Stavo eseguendo test di carico su un server tomcat. Il server ha memoria fisica 10G e spazio di scambio 2G. La dimensione heap (xms e xmx) era impostata su 3G prima e il server funzionava correttamente. Dato che ho ancora visto molti ricordi liberi e le prestazioni non erano buone, ho aumentato le dimensioni dell'heap a 7G e ho eseguito di nuovo il test di caricamento. Questa volta ho osservato che la memoria fisica è stata consumata molto rapidamente e il sistema ha iniziato a consumare spazio di swap. Successivamente, tomcat si è bloccato dopo aver esaurito lo spazio di swap. Ho incluso -XX:+HeapDumpOnOutOfMemoryError all'avvio di tomcat, ma non ho ricevuto alcun dump dell'heap. Quando ho controllato /var/log/messages, ho visto kernel: Out of memory: Kill process 2259 (java) score 634 or sacrifice child.Processo di tomcat ucciso dal kernel Linux dopo aver esaurito lo spazio di swap; non ottieni alcun errore JVM OutOfMemory

Per fornire maggiori informazioni, ecco quello che ho visto da Linux top comando quando dimensione heap impostato su 3G e 7G

XMS & xmx = 3G (che ha lavorato bene):

  • Prima Tomcat di partenza:

    Mem: 10129972k total, 1135388k used, 8994584k free, 19832k buffers 
    Swap: 2097144k total,  0k used, 2097144k free, 56008k cached 
    
  • Dopo Tomcat partenza:

    Mem: 10129972k total, 3468208k used, 6661764k free, 21528k buffers 
    Swap: 2097144k total,  0k used, 2097144k free, 143428k cached 
    PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
    2257 tomcat 20 0 5991m 1.9g 19m S 352.9 19.2 3:09.64 java 
    
  • Dopo aver avviato carico per 10 min:

    Mem: 10129972k total, 6354756k used, 3775216k free, 21960k buffers 
    Swap: 2097144k total,  0k used, 2097144k free, 144016k cached 
    PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
    2257 tomcat 20 0 6549m 3.3g 10m S 332.1 34.6 16:46.87 java 
    

xms & xmx = 7G (che ha causato arresto tomcat):

  • Prima di avviare tomcat:

    Mem: 10129972k total, 1270348k used, 8859624k free, 98504k buffers 
    Swap: 2097144k total,  0k used, 2097144k free, 74656k cached 
    
  • Dopo l'avvio Tomcat:

    Mem: 10129972k total, 6415932k used, 3714040k free, 98816k buffers 
    Swap: 2097144k total,  0k used, 2097144k free, 144008k cached 
    PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
    2310 tomcat 20 0 9.9g 3.5g 10m S 0.3 36.1 3:01.66 java 
    
  • Dopo l'avvio di carico per 10 minuti (a destra prima di Tomcat è stato ucciso):

    Mem: 10129972k total, 9960256k used, 169716k free,  164k buffers 
    Swap: 2097144k total, 2095056k used,  2088k free,  3284k cached 
    PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
    2310 tomcat 20 0 10.4g 5.3g 776 S 9.8 54.6 14:42.56 java 
    

Java e JVM Versione:

Java(TM) SE Runtime Environment (build 1.7.0_21-b11) 
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) 

Tomcat Versione:

6.0.36 

Linux Server:

Red Hat Enterprise Linux Server release 6.4 (Santiago) 

Quindi le mie domande sono:

  1. Perché questo problema accadere? Quando JVM esaurisce la memoria, perché non viene generato alcun errore OutOfMemoryError? E perché va dritto all'utilizzo dello swap?
  2. Perché topRES indica che java sta usando la memoria 5.3G, c'è molta più memoria consumata?

Ho indagato e cercato per un po ', ancora non riesco a trovare la causa principale di questo problema. Molte grazie!

+1

Una domanda migliore è perché Tomcat utilizza così tanta memoria? Puoi ancora ottenere un heap dump inviando il processo SIGQUIT (kill -3) o con 'jmap'. Eclipse MAT è probabilmente il modo più semplice per analizzare il dump se la maggior parte della memoria proviene da un unico punto. – AngerClown

+0

grazie! questa è una domanda migliore. Darò sicuramente un'occhiata alla discarica dell'heap. Ma sembra che ci sia anche un sacco di memoria inutilizzata. C'è un modo per controllarli? – baggiowen

+0

Non sono sicuro che possa essere d'aiuto, ma [VMMap] (http://technet.microsoft.com/en-us/sysinternals/dd535533.aspx) può mostrare l'utilizzo della memoria nativa di JVM. Puoi prima controllare le connessioni aperte con 'netstat' - la causa più probabile di una perdita con l'utilizzo di mem nativo sono i thread del contenitore web. – AngerClown

risposta

9

Perché questo problema accaduto? Quando JVM esaurisce la memoria, perché non viene generata OutOfMemoryException?

Non è la JVM che ha esaurito la memoria. È il sistema operativo host che ha esaurito le risorse relative alla memoria e sta prendendo l'azione drastica. Il sistema operativo non ha modo di sapere che il processo (in questo caso la JVM) è in grado di arrestarsi in modo ordinato quando viene detto "No" in risposta a una richiesta di memoria aggiuntiva. Deve uccidere duramente qualcosa altrimenti c'è un serio rischio che l'intero sistema operativo si blocchi.

In ogni caso, la ragione per cui non si è visto Oomes è che questa non è una situazione OOME. In realtà, la JVM ha già stato dato troppa di memoria dal sistema operativo, e non c'è modo di prendere di nuovo. Questo è il problema che il sistema operativo deve affrontare con i processi di hard-kill.

E perché utilizzare direttamente lo scambio?

Usa swap perché la richiesta di memoria virtuale totale dell'intero sistema non si adatta alla memoria fisica. Questo è un comportamento NORMALE per un sistema operativo UNIX/Linux.

Perché top RES mostra che java sta usando la memoria 5.3g, c'è molto di più di memoria consumata

I numeri FER possono essere un po 'fuorviante. Ciò a cui si riferiscono è la quantità di memoria fisica attualmente utilizzata dal processo ... escludendo elementi condivisi o condivisibili con altri processi. Il numero VIRT è più pertinente al tuo problema. Dice che la tua JVM sta usando 10.4g di virtual ... che è più rispetto alla memoria fisica disponibile sul tuo sistema.


Come dice l'altra risposta, è preoccupante per il fatto che non si ottiene un OOME. Anche se ne avessi uno, sarebbe imprudente fare qualsiasi cosa con esso.Un OOME è responsabile di danni collaterali all'applicazione/contenitore che è difficile da rilevare e difficile da recuperare. Ecco perché OOME è un Error non uno Exception.


Raccomandazioni:

  • non si tenta di utilizzare la memoria significativamente più virtuale che si ha memoria fisica, in particolare con Java. Quando una JVM esegue una garbage collection completa, toccherà la maggior parte di delle sue pagine VM, più volte in ordine casuale. Se hai sovra-allocato la tua memoria in modo significativo è probabile che causi il thrashing che uccide le prestazioni dell'intero sistema.

  • Aumentare lo spazio di scambio del sistema. (Ma potrebbe non essere d'aiuto ...)

  • Non provare a recuperare da OOMEs.

+0

grazie! ciò ha senso. ma quello che ancora non capisco è perché c'è così tanta differenza quando la dimensione dell'heap è impostata su 3G e 7G. osservando l'utilizzo della memoria prima di avviare tomcat, pensavo che il sistema operativo dovesse essere in grado di gestire l'heap 7G. – baggiowen

+1

La JVM è * in realtà * utilizzando 10.4G. Forse hai un sacco di memoria inutilizzata sotto copertura. Si noti inoltre che esiste una differenza simile a 3,5G tra la dimensione dell'heap richiesta e la dimensione VIRT osservata nel caso in cui si è utilizzato l'heap più piccolo. –

+1

grazie ancora. Ho appena realizzato che entrambi hanno una differenza di 3,5G. Quindi c'è un modo per scoprire l'utilizzo della memoria fuori dal mucchio sotto le coperte? Inoltre, stavo leggendo un altro post, che indica che l'RSS è più rilevante di VIRT. Sono confuso ora ... http: // StackOverflow.it/questions/561245/virtual-memory-usage-from-java-under-linux-too-much-memory-used? lq = 1 – baggiowen

1

Probabilmente ci sono altri processi sullo stesso computer che usano anche la memoria. Sembra che il tuo processo java raggiunga circa 5,3 GB prima che la macchina sia disperata dalla RAM e dallo scambio. (Probabilmente gli altri processi usano anche 12GB-5.3GB = 6.7GB) Quindi il tuo kernel Linux sacrifica il tuo processo java per mantenere in esecuzione altri processi. Il limite di memoria java non viene mai raggiunto in modo da non ottenere una OutOfMemoryException.

prendere in considerazione tutti i processi in esecuzione avete bisogno su tutta la macchina e regolare l'impostazione di conseguenza (abbastanza per lasciare spazio a tutti gli altri processi) il vostro Xmx. Forse 5 GB?

In ogni caso, il conteggio di OutOfMemoryExceptions che è trasportata è un odore di codice piuttosto male. Se ricordo correttamente, ottenere anche una sola OutOfMemoryException può lasciare la JVM in uno stato "all-bet-is-off" e probabilmente dovrebbe essere riavviato per non diventare instabile.

+0

grazie per la tua risposta! ma quando ho fatto 'top -a' (ordina per uso di memoria), non ho visto nessun altro processo che consumasse molta memoria. E se osservi l'utilizzo della memoria prima di avviare tomcat, è stata utilizzata solo la memoria da 1 G. – baggiowen

+0

Questo è un buon consiglio, ma la "OutOfMemoryException può lasciare la JVM in una parte" tutto-non-sono-stati "non corretta. In primo luogo, è "OutOfMemoryError", ma soprattutto, questo è un processo ordinato che non crea instabilità intrinseca. Il problema è che il tuo programma non è in grado di fare qualcosa di utile senza più memoria. Ma non è danneggiato o instabile in alcun modo. – erickson

+1

Il problema più grande con OutOfMemoryError è che può accadere in fase di esecuzione in profondità all'interno di alcuni java. * Classe framework o un'altra libreria di terze parti, che forse non dispone di un gestore di eccezioni pronto per eseguire la pulizia, ad esempio. Almeno questa è la spiegazione che ho avuto molti anni fa mentre stavo combattendo contro l'instabilità nelle librerie di terze parti innescate da tali errori. – faffaffaff

Problemi correlati