Dimensioni heap molto grandi di Java

Qualcuno ha esperienza con l’utilizzo di heap molto grandi, 12 GB o superiori in Java?

  • Il GC rende il programma inutilizzabile?
  • Quali parametri GC usi?
  • Quale JVM, Sun o BEA sarebbe più adatto a questo?
  • Quale piattaforma, Linux o Windows, offre prestazioni migliori in tali condizioni?
  • Nel caso di Windows c’è qualche differenza di prestazioni tra 64 bit Vista e XP con carichi di memoria così elevati?

Sono CEO di Azul Systems quindi sono ovviamente di parte sulla mia opinione su questo argomento! 🙂 Detto ciò…

Il CTO di Azul, Gil Tene, ha una buona panoramica dei problemi associati a Garbage Collection e una revisione di varie soluzioni nella sua Understanding Java Garbage Collection e cosa puoi fare su di esso , e ci sono ulteriori dettagli in questo articolo: http: // http://www.infoq.com/articles/azul_gc_in_detail .

Il C4 Garbage Collector di Azul nella nostra JVM di Zing è sia parallelo che simultaneo e utilizza lo stesso meccanismo GC sia per le nuove che per le vecchie generazioni, lavorando contemporaneamente e compattando in entrambi i casi. La cosa più importante è che C4 non ha alcun ripiego nel mondo. Tutta la compattazione viene eseguita in concomitanza con l’applicazione in esecuzione. Abbiamo clienti in esecuzione molto grandi (centinaia di GByte) con tempi di pausa GC del caso peggiore <10 msec e, a seconda dell'applicazione, spesso meno di 1-2 msec.

Il problema con CMS e G1 è che a un certo punto la memoria dell’heap di Java deve essere compattata, e entrambi i garbage collector fermano il mondo / STW (cioè mettono in pausa l’applicazione) per eseguire la compattazione. Quindi, mentre CMS e G1 possono far uscire le pause STW, non le eliminano. Azul’s C4, tuttavia, elimina completamente le pause STW ed è per questo che Zing ha pause di GC così basse anche per enormi dimensioni di heap.

Abbiamo un’applicazione che assegniamo 12-16 Gb per ma in realtà raggiunge solo 8-10 durante il normale funzionamento. Usiamo Sun JVM (provato IBM ed è stato un po ‘disastroso ma potrebbe essere stata solo ignoranza da parte nostra … ho amici che lo giurano – che lavorano in IBM). Finché fornisci il respiro della tua app, la JVM può gestire grandi dimensioni di heap con un GC non eccessivo. Un sacco di memoria extra è la chiave.
Linux è quasi sempre più stabile di Windows e quando non è stabile è molto più facile capire perché. Anche Solaris è solido come la roccia e anche DTrace 🙂 Con questi tipi di carichi, perché mai useresti Vista o XP? Stai solo chiedendo dei guai. Non facciamo niente di speciale con i parametri GC. Impostiamo l’allocazione minima in modo che sia uguale al massimo in modo che non tenti continuamente di ridimensionare, ma è così.

Ho usato più di 60 GB di heap dimensioni su due diverse applicazioni sotto Linux e Solaris rispettivamente utilizzando versioni a 64 bit (ovviamente) di Sun 1.6 JVM.

Non ho mai riscontrato problemi di garbage collection con l’applicazione basata su Linux tranne quando si spingeva verso l’alto vicino al limite della dimensione heap. Per evitare i problemi di thrashing inerenti a questo scenario (troppo tempo dedicato alla raccolta dei dati inutili), ho semplicemente ottimizzato l’utilizzo della memoria in tutto il programma in modo che l’utilizzo massimo fosse circa il 5-10% al di sotto del limite di 64 GB.

Con un’applicazione diversa in esecuzione in Solaris, tuttavia, ho riscontrato notevoli problemi di raccolta dei dati inutili che hanno reso necessaria la modifica. Questo consisteva principalmente in tre passaggi:

  1. Abilitazione / forzatura dell’utilizzo del garbage collector parallelo tramite le opzioni -XX: + UseParallelGC -XX: + UseParallelOldGC JVM, oltre al controllo del numero di thread GC utilizzati tramite l’opzione -XX: ParallelGCThreads. Vedi ” Ottimizzazione dell’accumulo di una macchina virtuale di Java SE 6 HotSpot ” per maggiori dettagli.

  2. Impostazione ampia e apparentemente ridicola delle variabili locali su “null” dopo che non sono più necessarie. La maggior parte di queste erano variabili che avrebbero dovuto essere idonee per la garbage collection dopo essere uscite dal campo di applicazione, e non erano situazioni di perdita di memoria dal momento che i riferimenti non venivano copiati. Tuttavia, questa strategia di “hand-holding” per aiutare la raccolta dei rifiuti era inspiegabilmente necessaria per qualche ragione per questa applicazione sotto la piattaforma Solaris in questione.

  3. Uso selettivo della chiamata al metodo System.gc () nelle sezioni di codice chiave dopo lunghi periodi di allocazione temporanea dell’object. Sono a conoscenza degli avvertimenti standard contro l’utilizzo di queste chiamate e dell’argomento secondo cui dovrebbero essere normalmente non necessari, ma ho riscontrato che sono fondamentali per la raccolta di rifiuti inutili durante l’esecuzione di questa applicazione a uso intensivo di memoria.

I tre passaggi precedenti hanno reso ansible mantenere questa applicazione contenuta e in esecuzione produttiva a circa 60 GB di utilizzo dell’heap anziché crescere senza controllo fino al limite della dimensione dell’heap di 128 GB. Il garbage collector parallelo in particolare è stato molto utile poiché i principali cicli di raccolta dei dati inutili sono costosi quando vi sono molti oggetti, ovvero il tempo necessario per la raccolta di dati importanti è una funzione del numero di oggetti nell’heap.

Non posso commentare altri problemi specifici della piattaforma su questa scala, né ho usato JVM non Sun (Oracle).

12 GB non dovrebbe essere un problema con un’implementazione JVM decente come l’hotspot di Sun. Ti consiglio di usare il Concorrent Mark and Sweep colllector (-XX: + UseConcMarkSweepGC) quando usi una SUN VM.Otherwies potresti dover affrontare lunghe fasi di “stop the world”, dove tutti i thread sono stati fermati durante un GC.

Il sistema operativo non dovrebbe fare una grande differenza per le prestazioni del GC.

Avrai bisogno ovviamente di un sistema operativo a 64 bit e una macchina con sufficiente RAM fisica.

Raccomando anche di prendere in considerazione un heap dump e vedere dove l’utilizzo della memoria può essere migliorato nella tua app e analizzare il dump in qualcosa come Eclipse’s MAT . Ci sono alcuni articoli sulla pagina MAT su come iniziare a cercare perdite di memoria. Puoi usare jmap per ottenere il dump con qualcosa come …

jmap -heap:format=b pid 

Come accennato in precedenza, se si dispone di un programma non interattivo, il garbage collector (GC) predefinito (compattazione) predefinito dovrebbe funzionare correttamente. Se hai un programma interattivo e tu (1) non alleni la memoria più velocemente di quanto il GC può tenere aggiornato, e (2) non creare oggetti temporanei (o raccolte di oggetti) troppo grandi (rispetto al totale massima memoria JVM) affinché il GC funzioni, quindi il CMS è fatto per te.

Ti imbatti in problemi se hai un programma interattivo in cui il GC non ha abbastanza spazio per respirare. Questo è vero indipendentemente da quanta memoria hai, ma più memoria hai, peggio diventa. Questo perché quando si esaurisce la memoria, CMS esaurirà la memoria, mentre i GC di compattazione (incluso G1) metteranno in pausa tutto finché tutta la memoria non sarà stata controllata per la spazzatura. Questa pausa stop-the-world diventa più grande più memoria hai. Fidati di me, non vuoi che i tuoi servlet si fermino per più di un minuto. Ho scritto una risposta StackOverflow dettagliata su queste pause in G1.

Da allora, la mia azienda è passata a Azul Zing. Non è ancora in grado di gestire il caso in cui la tua app ha davvero bisogno di più memoria di quella che hai, ma fino a quel momento corre come un sogno.

Ma, naturalmente, Zing non è gratuito e la sua salsa speciale è brevettata. Se hai molto più tempo del denaro, prova a riscrivere l’app per utilizzare un cluster di JVM.

All’orizzonte, Oracle sta lavorando su un GC ad alte prestazioni per cumuli da più gigabyte. Tuttavia, ad oggi non è un’opzione.

Se passi a 64-bit utilizzerai più memoria. I puntatori diventano 8 byte invece di 4. Se si creano molti oggetti questo può essere notevole visto che ogni object è un riferimento (puntatore).

Di recente ho assegnato 15 GB di memoria in Java utilizzando la JVM Sun 1.6 senza problemi. Sebbene sia tutto solo assegnato una volta. Non viene allocata o rilasciata molta più memoria dopo l’importo iniziale. Era su un Linux, ma immagino che Sun JVM funzionerà altrettanto bene su Windows a 64 bit.

un articolo da sun su java 6 può aiutarti: http://java.sun.com/developer/technicalArticles/javase/troubleshoot/

Dovresti provare a eseguire visualgc contro la tua app. È uno strumento di visualizzazione dell’heap che fa parte del download di jvmstat su http://java.sun.com/performance/jvmstat/

È molto più semplice della lettura dei log di GC.

Ti aiuta rapidamente a capire come funzionano le parti (generazioni) dell’heap. Mentre l’heap totale può essere di 10 GB, le varie parti dell’heap saranno molto più piccole. I GC nella porzione di Eden del mucchio sono relativamente economici, mentre i GC completi nella vecchia generazione sono costosi. Dimensionare il tuo heap in modo che l’Eden sia grande e la vecchia generazione non venga quasi mai toccata è una buona strategia. Ciò può causare un heap complessivo molto grande, ma che diamine, se la JVM non tocca mai la pagina, è solo una pagina virtuale e non deve occupare la RAM.

Un paio di anni fa, ho confrontato JRockit e Sun JVM per un heap di 12G. JRockit ha vinto e il supporto per le enormi pagine di Linux ha reso il nostro test più veloce del 20%. YMMV come il nostro test era molto intensivo per la memoria e il processore ed era principalmente single-threaded.

ecco un articolo su gc DA uno dei Java Champions – http://kirk.blog-city.com/is_your_concurrent_collector_failing_you.htm

Kirk, l’autore scrive “Mandami i tuoi log del GC

Attualmente sono interessato a studiare i log GC prodotti da Sun JVM. Dal momento che questi registri non contengono informazioni rilevanti per il business, dovrebbero essere semplici preoccupazioni sulla protezione delle informazioni proriarie. Tutto quello che chiedo è che con il log si menzioni il sistema operativo, le informazioni complete sulla versione per JRE e tutte le opzioni della riga di comando relative a heap / gc che sono state impostate. Mi piacerebbe anche sapere se stai eseguendo Grails / Groovey, JRuby, Scala o qualcosa di diverso o lungo Java. L’impostazione migliore è -Xloggc :. Si prega di essere consapevoli del fatto che questo registro non si arrotola quando raggiunge il limite di dimensioni del sistema operativo. Se trovo qualcosa di interessante, sarò felice di darti una rapida sinossi in cambio. ”

La memoria massima che XP può indirizzare è di 4 gig ( qui ). Quindi potresti non voler usare XP per questo (usa un sistema operativo a 64 bit).

Sun ha avuto un jvm itanium a 64 bit per un po ‘sebbene l’itanium non sia una destinazione popolare. Le JVM a 64 bit di Solaris e Linux dovrebbero essere ciò che dovresti desiderare.
Alcune domande

1) la tua applicazione è stabile?
2) hai già provato l’app in una JVM a 32 bit?
3) è OK eseguire più JVM sulla stessa scatola?

Mi aspetto che il sistema operativo a 64 bit di Windows diventi stabile in circa un anno o giù di lì, ma fino a quel momento, solaris / linux potrebbe essere una scelta migliore.