HP-UX 11i Conoscenza on demand: best practice per l'ottimizzazione delle prestazioni direttamente dai nostri laboratori
←
→
Trascrizione del contenuto della pagina
Se il tuo browser non visualizza correttamente la pagina, ti preghiamo di leggere il contenuto della pagina quaggiù
HP-UX 11i Conoscenza on demand: best practice per l'ottimizzazione delle prestazioni direttamente dai nostri laboratori Serie sviluppatori Gestione memoria Java: meccanismi di funzionamento e prestazioni -- Testo del webcast Buongiorno a tutti e grazie ai partecipanti in remoto per essersi uniti a noi, vi porgo il mio benvenuto. In questa sessione parleremo dei meccanismi di funzionamento e delle prestazioni della gestione della memoria Java. La presentazione odierna mira innanzitutto a illustrare il funzionamento dei diversi criteri di garbage collection in HP-UX, ovvero un passaggio fondamentale per chiunque lavori all'analisi e al miglioramento delle prestazioni di un'applicazione Java. La presentazione, d'altro canto, non si sofferma in dettaglio sugli strumenti forniti da HP per l'analisi di problemi prestazionali relativi a garbage collection. Oggi riceverete informazioni preliminari essenziali per il futuro utilizzo di qualsiasi strumento di analisi di garbage collection. È infatti necessario disporre di una buona comprensione dei meccanismi di funzionamento di garbage collection per poter interpretare i dati forniti dagli appositi strumenti e agire di conseguenza per sintonizzare l'applicazione che si sta sviluppando. [AVANTI] Inizieremo dunque con una semplice introduzione e una panoramica sulla gestione della memoria Java, quindi analizzeremo più da vicino i garbage collector generazionali al fine di comprenderne appieno il funzionamento e vedere cosa succede quando un'applicazione è sottoposta a un ciclo di garbage collection. Successivamente, vedremo in dettaglio tutti i criteri di garbage collection supportati dalla JVM (Java Virtual Machine) HP e illustreremo i valori predefiniti di ciascun criterio. In questo modo, potrete individuare meglio la parte dell'heap della vostra applicazione che occorre sincronizzare. Infine, termineremo con un ripasso delle linee guida fondamentali volte a garantire buone prestazioni di gestione della memoria da parte dell'applicazione da voi sviluppata. [DIAPOSITIVA SUCCESSIVA] Partiamo dunque con una panoramica sulla gestione della memoria per applicazioni scritte in Java. La gestione della memoria Java è comunemente denominata garbage collection. Nel corso della presentazione odierna utilizzerò entrambe le espressioni. La premessa fondamentale da fare quando si parla di gestione della memoria Java è che questa viene gestita automaticamente da JVM. A differenza dei linguaggi C e C++, dove sono gli sviluppatori a gestire in modo esplicito l'allocazione e la deallocazione di memoria tramite le funzioni malloc/free o new/delete, gli sviluppatori che lavorano in Java non devono intervenire in alcun modo: i requisiti di memoria dell'applicazione
Esistono vari tipi di garbage collector, ciascuno dei quali presenta determinati pro e contro, ad esempio i garbage collector per reference count, che si affidano al mantenimento di un contatore di riferimenti memorizzato nell'intestazione di ogni oggetto; il valore del contatore viene incrementato/decrementato ogniqualvolta si procede al riferimento o alla risoluzione del riferimento a un oggetto; gli oggetti con riferimenti pari a zero sono considerati inanimati e sottoposti a garbage collection. Si tratta dunque di un metodo semplice e piuttosto rapido di gestione della memoria, ma appare subito chiaro che non consente di gestire con sufficiente efficacia la liberazione dello spazio, per esempio non esegue la compattazione. Vi sono poi i garbage collector cosiddetti di eliminazione identificazioni. A differenza dei raccoglitori per reference count, questi provvedono periodicamente all'arresto totale di un'applicazione in esecuzione, eseguendo una rapida scansione degli oggetti per individuare e contrassegnare quelli non più raggiungibili e provvedendo poi alla loro eliminazione allo scopo di liberare spazio. Un terzo tipo di collector è costituito dai raccoglitori compatti eliminazione identificazioni per la copia che, a differenza dei collector di sola eliminazione identificazioni, che gestiscono la memoria dell'applicazione in uno spazio dedicato, suddividono lo spazio di allocazione degli oggetti in due aree distinte, dove una delle due funge da normale raccoglitore eliminazione identificazioni, ma invece di limitarsi a identificare e rimuovere gli oggetti inanimati, copia tutti gli oggetti attivi o superstiti nella seconda area. La copia sequenziale degli oggetti attivi nella seconda area rimuove la frammentazione e compatta la memoria. Al termine della copia viene ripresa l'esecuzione dell'applicazione e l'allocazione dei nuovi oggetti viene indirizzata all'interno dell'area compattata. Il collector alterna la copia degli oggetti superstiti tra le due diverse aree. Sia i raccoglitori eliminazione identificazioni, sia i raccoglitori compatti eliminazione identificazioni sono del tipo cosiddetto di arresto, in quanto è necessario che arrestino l'esecuzione dell'applicazione prima poter procedere all'identificazione ed eliminazione. Un quarto tipo di garbage collector è costituito dai raccoglitori eliminazione identificazioni simultanei, ideati per ridurre il tempo di interruzione delle applicazioni durante la garbage collection. Benché denominati collector simultanei, non lo sono del tutto, in quanto anche con questi raccoglitori è necessario interrompere l'esecuzione dell'applicazione, benché la durata di tale interruzione sia alquanto ridotta. Il garbage collector fornito con JVM HP è un raccoglitore generazionale per la copia, in quanto costituito da un raccoglitore compatto eliminazione identificazioni, con la differenza che suddivide il Java heap in più di due aree. [AVANTI] Passiamo dunque ad analizzare più in dettaglio i garbage collector generazionali e il loro funzionamento. [AVANTI] I garbage collector generazionali suddividono il Java heap in cinque aree, o generazioni, ben distinte: una generazione nuova/giovane suddivisa a sua volta in un eden space e due altre aree sopravvivenza denominate TO e FROM, un'area vecchia e uno spazio permanente separato. L'heap viene suddiviso in questo modo perché sappiamo, dall'analisi di un vasto campione di applicazioni, che gran parte degli oggetti ha vita breve, pertanto, dato che il costo di un ciclo di garbage collection è direttamente proporzionale al numero di oggetti che occorre esaminare, ovvero, più sono gli oggetti da identificare ed eliminare maggiore sarà il costo, tendiamo a ottimizzare il costo della garbage collection velocizzando il caso più comune, vale a dire il costo di identificazione ed eliminazione degli ultimi oggetti assegnati a una piccola parte del Java heap e cioè la generazione nuova/giovane. Le dimensioni totali dell'heap sono controllate da due flag JVM o opzioni della riga di comando: -Xms e –Xmx; - Xms controlla le dimensioni iniziali dell'heap, mentre –Xmx controlla le dimensioni massime dell'heap, come
illustrato qui. Se i due flag JVM non sono impostati sullo stesso valore, JVM inizializza l'heap in base al valore di –Xms, quindi, se necessario, espande periodicamente l'heap fino al raggiungimento delle dimensioni massime. Dalla riga di comando è inoltre possibile impostare le dimensioni della generazione nuova/giovane specificando il valore di –Xmn e le dimensioni dello spazio permanente specificando il valore di –XX:PermSize. Nella generazione nuova/giovane risiedono tutti i nuovi oggetti creati, mentre gli oggetti sopravvissuti alla garbage collection, i cosiddetti oggetti longevi, vengono copiati nella vecchia generazione. Lo spazio permanente è riservato alle informazioni relative alle classi dell'applicazione, utilizzate direttamente da JVM. [AVANTI] Come già accennato, gli spazi To e FROM sono anche detti spazi superstiti e le loro dimensioni vengono stabilite in base alle dimensioni della nuova generazione. Il flag della riga di comando –XX:SurvivorRatio consente di impostare le dimensioni di uno spazio superstite in base alle dimensioni di eden space. Un altro flag fondamentale della riga di comando da tenere a mente è –XX:MaxTenuringThreshold; il valore impostato tramite questo flag consente di specificare l'età di un oggetto superstite, ovvero il numero di volte che l'oggetto è sopravvissuto a una garbage collection minore. Capirete meglio quanto accennato finora durante la proiezione delle prossime diapositive, dove viene spiegato in dettaglio il meccanismo di funzionamento dei garbage collector generazionali. [AVANTI] In base a quanto detto finora sull'heap, è evidente che esistono due tipi di garbage collection, aventi ciascuna un costo molto diverso: una collection minore, corrispondente al riempimento della nuova/giovane generazione, e una collection maggiore, attivata quando si riempie tutto l'heap. [AVANTI] Ora che abbiamo appreso in che modo il garbage collector generazionale suddivide l'heap, analizziamone più in profondità il funzionamento. Come già accennato, tutti gli oggetti nuovi vengono inizialmente assegnati alla nuova generazione, o più propriamente, tutti gli oggetti nuovi vengono assegnati direttamente all'eden space della nuova generazione. Nell'illustrazione seguente del Java heap sono presenti diversi oggetti "in rosso" assegnati all'eden space a seguito dell'esecuzione di un'applicazione per un dato periodo di tempo. Esclusivamente ai fini di questo esempio, poniamo che la soglia di occupazione sia stata impostata su un valore basso, ad esempio 2. L'applicazione è stata dunque eseguita per un dato periodo di tempo, durante il quale ha creato nuovi oggetti che sono stati inizialmente posizionati nell'eden space. Cosa succede quando l'eden space si riempie? [AVANTI] Quando l'eden space è pieno, JVM sospende l'esecuzione dell'applicazione. Questa operazione è stata già definita "di arresto" durante la descrizione dei diversi tipi di garbage collector. Dopo avere interrotto l'esecuzione dell'applicazione Java, JVM analizza tutti gli oggetti presenti nell'eden space contrassegnando quelli ancora attivi, ovvero quelli a cui fanno riferimento altri oggetti dell'applicazione. Gli oggetti attivi presenti in questa illustrazione dell'heap sono indicati con una freccia rivolta verso di loro. Cosa succede dopo che JVM ha contrassegnato tutti gli oggetti attivi?
[AVANTI] A questo punto, tutti gli oggetti attivi vengono copiati in una delle aree sopravvivenza. [AVANTI] Gli oggetti che rimangono all'interno dell'eden space dopo che tutti gli oggetti attivi sono stati copiati in un'area sopravvivenza sono quelli considerati inanimati e che devono quindi essere sottoposti a garbage collection. [AVANTI] Gli oggetti inanimati presenti nell'eden space vengono sottoposti a garbage collection o a un'operazione di pulizia mediante una cosiddetta collection minore, che interviene solo su una piccola parte dell'heap, in particolare l'eden space. Agli oggetti attivi non sottoposti all'operazione di pulizia, vale a dire quelli copiati in un'area sopravvivenza, viene assegnata un'età pari a 1, che rappresenta la loro soglia di occupazione. Al termine dell'operazione di pulizia, JVM riprende l'esecuzione dell'applicazione che era stata interrotta e pertanto nella generazione nuova/giovane vengono creati nuovi oggetti. [AVANTI] I nuovi oggetti, rappresentati in questa diapositiva sotto forma di blocchi gialli, riempiono gradatamente l'eden space della nuova generazione. Cosa succede a questo punto? JVM interrompe nuovamente l'esecuzione dell'applicazione e si prepara ad eseguire una nuova collection minore (o operazione di pulizia). [AVANTI] Tutti gli oggetti attivi presenti nell'eden space e nella prima area sopravvivenza vengono contrassegnati, quindi copiati nella seconda area sopravvivenza e la rispettiva età o soglia di occupazione viene aggiornata di conseguenza. [AVANTI] Notate come, dopo un'operazione di pulizia o una collection minore, si arrivi sempre allo svuotamento completo dell'eden space e di un'area sopravvivenza. Notate inoltre che a questo punto uno degli oggetti è sopravvissuto a due collection minori, raggiungendo perciò la propria soglia di occupazione. Pertanto, se rimane attivo anche dopo la terza collection minore, verrà spostato nella vecchia generazione. Quindi, ora che la seconda operazione di pulizia o collection minore è stata completata, JVM ripristina l'esecuzione dell'applicazione e nell'eden space vengono posizionati nuovi oggetti. [AVANTI] In questa diapositiva i nuovi oggetti sono raffigurati come blocchi azzurri all'interno dell'eden space. [AVANTI] Una volta riempito l'eden space, JVM interrompe per la terza volta l'esecuzione dell'applicazione, tutti gli oggetti attivi vengono identificati e contrassegnati, quindi quelli che hanno raggiunto la propria soglia di occupazione
vengono dapprima copiati nell'area vecchia. [AVANTI] Qui viene mostrato come un oggetto sopravvissuto a tre operazioni di pulizia o collection minori venga spostato nell'area vecchia. Da questo momento in poi, l'oggetto sarà considerato un oggetto longevo e non verrà più sottoposto a scansione o copiato in un'altra area dopo l'ennesima operazione di pulizia. [AVANTI] Dopo che l'oggetto longevo è stato copiato nell'area vecchia, tutti gli oggetti attivi presenti nell'eden space e in un'area sopravvivenza vengono identificati e copiati nella seconda area sopravvivenza. [AVANTI] Quindi, come in precedenza, viene aggiornata l'età degli oggetti superstiti. [AVANTI] E viene avviata un'operazione di pulizia per la rimozione di tutti gli oggetti inanimati residui dall'eden space e da una delle due aree sopravvivenza. Quindi, JVM riprende l'esecuzione dell'applicazione, con conseguente assegnazione di altri oggetti alla generazione nuova/giovane. [AVANTI] Trascorso un certo periodo d'esecuzione la vecchia generazione si riempie, o quasi, come pure l'eden space. A questo punto, JVM interrompe ancora una volta l'esecuzione dell'applicazione e avvia un ciclo di garbage collection maggiore (o completa). Nel corso di questo ciclo, tutti gli oggetti dell'heap, tanto quelli presenti nell'area nuova/giovane che quelli presenti nella vecchia area, vengono sottoposti a scansione e contrassegnati. [AVANTI] Quindi, il garbage collector elimina tutti gli oggetti inanimati e ricompatta l'area vecchia, come illustrato in questa diapositiva. Le garbage collection maggiori, o complete, hanno un costo molto superiore alle garbage collection minori, principalmente perché devono elaborare tutti gli oggetti presenti nell'heap e perché soggette a un consumo aggiuntivo di risorse per la compattazione dell'area vecchia. A questo punto abbiamo illustrato il funzionamento dei garbage collector generazionali compatti di eliminazione identificazioni, ma prima di passare ai meccanismi di funzionamento di tutti i criteri di garbage collection supportati, analizziamo un corner case per vedere come lo risolve internamente JVM. [AVANTI] Nel caso preso in esame, dopo che si è riempita la nuova generazione si può verificare una situazione per cui tutti gli oggetti presenti nell'eden space e in un'area sopravvivenza sono attivi. Una normale operazione di pulizia tenterebbe di copiare tutti gli oggetti attivi nella seconda area sopravvivenza, come mostrato in questa diapositiva, ma in questo caso ciò non è ovviamente possibile. Per poter gestire casi di questo tipo, per impostazione predefinita JVM mette da parte una porzione dell'area vecchia le cui dimensioni equivalgano all'eden space più un'area sopravvivenza. In genere, tutto questo spazio
va sprecato, soprattutto perché è raro che tutti gli oggetti presenti nell'eden space e in un'area sopravvivenza rimangano attivi dopo una garbage collection minore. Se sapete per certo che la vostra applicazione non presenta una tale condizione, potete ridurre o eliminare del tutto lo spazio messo da parte nella vecchia generazione per la gestione di questo caso. Utilizzate a questo scopo il parametro –XX:MaxLiveObjectEvacuationRatio, che vi consente di specificare la percentuale della vecchia generazione che desiderate mettere da parte, oppure specificate l'opzione –Xoptgc, mediante la quale, essenzialmente, non viene messo da parte alcuno spazio nella vecchia generazione per la gestione del caso. Ricordate inoltre che mantenere al massimo le dimensioni della vecchia generazione ha il vantaggio di ridurre il numero di garbage collection complete e i relativi costi. [AVANTI] Esamineremo ora i meccanismi di funzionamento dei criteri di garbage collection di HP-UX, implementati per lo più sotto forma di garbage collector generazionali compatti di eliminazione identificazioni, con un funzionamento in larga parte analogo a quanto già spiegato. Vi sono tuttavia alcune differenze nel funzionamento dei criteri di eliminazione identificazioni simultanei che vi spiegherò durante l'analisi dei criteri stessi. [AVANTI] In ambiente HP-UX, Java Virtual Machine supporta quattro diversi criteri di garbage collection: un criterio seriale a thread singolo, un criterio parallelo ad alta velocità di nuova generazione, un criterio parallelo ad alta velocità di vecchia generazione e un criterio low pause di eliminazione identificazioni simultaneo. La presenza di diversi criteri è dovuta al fatto che non è indicato disporre di un solo criterio di garbage collection in grado di eseguire il proprio compito con ogni tipo di applicazione. Esaminiamo ora in che modo viene implementato ciascun criterio. [AVANTI] È possibile attivare il criterio seriale di garbage collection specificando l'opzione –XX:+UseSerialGC nella riga di comando Java. Prima del JDK 1.4.2, questo era il criterio di garbage collection predefinito sulle piattaforme HP- PA e Integrity. Come illustrato qui, con questo criterio le garbage collection maggiori e minori sono entrambe a thread singolo. [AVANTI] Il secondo criterio, denominato criterio parallelo di nuova generazione, è stato introdotto nelle versioni più recenti del JDK 1.4.1 ed è ora il criterio predefinito dei server a 2 o più CPU a partire dal JDK 1.4.2. Gli utenti possono attivare in modo esplicito questo criterio specificando l'opzione –XX:+UseParNewGC nella riga di comando Java. Se si ricorre al criterio parallelo di garbage collection ad alta velocità di nuova generazione, le operazioni di pulizia (o garbage collection minori della nuova generazione) vengono eseguite in parallelo. Invece, la garbage collection della vecchia generazione, nel momento in cui subentra una garbage collection maggiore (o completa) rimane a thread singolo, analogamente a quanto avviene con il criterio di garbage collection seriale. [AVANTI]
Specificando l'opzione –XX:+UseParallelOldGC nella riga di comando Java, viene attivato un collector parallelo ad alta velocità per cui le operazioni di pulizia (o garbage collection minori) e garbage collection maggiori della vecchia generazione vengono eseguite in parallelo su macchine a 2 o più core. Nella prossima sezione vi illustrerò come controllare il numero di thread paralleli quando si parla di valori predefiniti dei criteri. [AVANTI] Specificando l'opzione –XX:+UseConcMarkSweepGC nella riga di comando di Java viene attivato il criterio low pause di eliminazione identificazioni simultanee del JVM. Tale criterio utilizza un collector multithread per la giovane generazione e un collector low pause quasi simultaneo per la vecchia generazione. Il collector della vecchia generazione è definito quasi simultaneo perché contiene due pause di arresto estremamente brevi, come illustrato in questa diapositiva. La fase simultanea di questo criterio è costituita da 4 fasi principali. [AVANTI] La prima fase, come indicato, interrompe l'esecuzione dell'applicazione per un breve periodo di tempo e richiama un thread singolo per una prima identificazione, rivolta a un sottoinsieme di tutti gli oggetti attivi. Quando osserviamo con i nostri strumenti l'output di verboseGC, identifichiamo questa fase come STW 1 (ovvero Stop The World 1, o pausa di arresto 1). [AVANTI] Dopo che è stato identificato un primo sottoinsieme di oggetti attivi, JVM ripristina l'esecuzione dell'applicazione richiamando un thread simultaneo per l'individuazione di tutti gli oggetti attivi raggiungibili dall'insieme di oggetti attivi individuati nella fase iniziale. In questa fase viene inoltre eseguita un'operazione di pulizia preliminare, ovvero una scansione proattiva di tutti gli oggetti aggiornati durante la fase di identificazione simultanea. Tale operazione preliminare è volta a ridurre la pausa prevista nella fase 3 [AVANTI] Nella fase 3, JVM interrompe ancora una volta l'esecuzione dell'applicazione e richiama thread multipli per una rapida reidentificazione parallela, allo scopo di individuare il maggior numero di oggetti attivi sfuggiti durante la fase di identificazione simultanea. Non dimenticate che, nel corso della fase di identificazione simultanea, l'applicazione è ancora in esecuzione e crea nuovi oggetti. Quando osserviamo con i nostri strumenti l'output di verboseGC, identifichiamo questa fase come STW 2 (ovvero Stop The World 2, o pausa di arresto 2). [AVANTI] Dopo la fase di reidentificazione parallela, JVM ripristina per la seconda volta l'esecuzione dell'applicazione e richiama un thread singolo simultaneo per l'eliminazione di tutti gli oggetti inanimati. [AVANTI] I nostri strumenti attuali non dispongono di una vista unica in grado di mostrare le diverse fasi di arresto di eliminazione identificazioni simultanee correlate all'utilizzo dell'heap. Per ovviare a questo problema, io di solito sovrappongo l'utilizzo dell'heap alla vista della durata, come illustrato in questa diapositiva, e collego visivamente l'asse della durata di entrambe le viste per ottenere il quadro di quanto avviene nel Java heap durante ogni fase. [AVANTI]
Chi ha già iniziato a utilizzare il JDK 6, è bene che conosca l'esistenza di due nuove funzioni dei criteri di eliminazione identificazioni simultanee. A partire dal JDK 6, infatti, sarà possibile specificare più thread di eliminazione simultanei semplicemente specificando l'opzione –XX:ParallelCMSThreads nella riga di comando Java. Così facendo si velocizza in modo considerevole il garbage collector di eliminazione identificazioni simultanee durante l'esecuzione su server multi core. Nel JDK 6, inoltre, è ora possibile ignorare l'impostazione predefinita di gestione delle garbage collection esplicite (ad esempio, una chiamata a System.gc() dall'interno dell'applicazione) al fine di richiamare un criterio low pause di eliminazione identificazioni simultanee invece di una garbage collection completa, caratterizzata da costi elevati, anche di compattazione dell'area vecchia. Per attivare questa nuova funzione è sufficiente specificare l'opzione –XX:+ExplicitGCInvokesConcurrent nella riga di comando Java. [AVANTI] Un'altra novità introdotta nel JDK 6 che influisce direttamente sul collector di eliminazione identificazioni simultanee riguarda la gestione delle aree sopravvivenza e di occupazione. Nei JDK precedenti alla versione 6, specificando il criterio di garbage collection di eliminazione identificazioni simultanee si provvedeva essenzialmente a disattivare le aree sopravvivenza (in breve, specificando il collector di eliminazione identificazioni simultanee, il tasso di sopravvivenza veniva impostato su 1024, con una diminuzione progressiva delle dimensioni delle aree sopravvivenza fino alla loro disattivazione). In questo modo, gli oggetti venivano occupati prematuramente, con conseguente aumento della pressione sulla vecchia generazione. A partire dal JDK versione 6, le aree sopravvivenza vengono riattivate, consentendone, se necessario, la sincronizzazione da parte degli utenti. [AVANTI] Prima di concludere la nostra discussione su eliminazione identificazioni simultanee, bisogna che vi spieghi come viene gestita la "garanzia della giovane generazione" quando si specifica il garbage collector di eliminazione identificazioni simultanee. Se ricordate, nel caso dei garbage collector non simultanei, la mancanza di spazio riservato nella vecchia generazione nel caso in cui tutti gli oggetti della nuova generazione siano sopravvissuti a una garbage collection minore, comporta l'esaurimento della memoria disponibile con conseguente terminazione del software JVM. Con i garbage collector di eliminazione identificazioni simultanee, invece, quando si verifica questa situazione viene dapprima avviata in extremis una garbage collection minore, per verificare l'eventuale inattività di alcuni oggetti da quando si era precedentemente tentato un innalzamento di livello. Qualora questa garbage collection in extremis non sortisca l'esito di liberare spazio sufficiente nella vecchia generazione, il garbage collector provvederà a una garbage collection maggiore, seguita dalla compattazione; se anche in questo caso non viene liberata una quantità di spazio sufficiente, il software JVM viene terminato per un errore di memoria insufficiente. [AVANTI] Ora analizzeremo le impostazioni predefinite di ogni criterio di garbage collection, al fine di comprendere meglio il comportamento del garbage collector se non si provvede a modificarle in modo esplicito. [AVANTI] Se nella riga di comando non viene specificato alcun parametro di garbage collection, le dimensioni iniziali del Java heap vengono impostate automaticamente su un sessantaquattresimo del totale della memoria fisica disponibile nella scatola, ma limitate a 1 GB; analogamente, le dimensioni massime del Java heap vengono impostate automaticamente su un quarto del totale della memoria disponibile nella scatola, sempre con il limite di 1 GB. Sulle macchine dotate di più di 2 core (o processori),viene selezionato per impostazione predefinita il nuovo
criterio di garbage collection parallela. Inoltre, sempre per impostazione predefinita, il software JVM attiva il criterio di dimensionamento adattivo, che tenta di modificare efficacemente le dimensioni di eden space e le aree sopravvivenza, oltre che le soglie di occupazione, in base a regole euristiche interne di JVM. [AVANTI] Successivamente, tenteremo di comprendere il modo in cui JVM imposta il numero di thread dei garbage collection paralleli quando viene attivato uno dei collettori ad alta velocità. Se il server sui cui è implementato Java ha fino a un massimo di 8 core (o processori), JVM inizializzerà tanti thread di garbage collection paralleli quanti sono i core presenti nella scatola. Nei casi in cui Java sia attivato su server aventi più di 8 core, il numero dei thread di garbage collection paralleli viene impostato da JVM in base alla formula qui illustrata. [AVANTI] JVM utilizza lo stesso meccanismo per stabilire il numero di thread di garbage collection paralleli relativi al collettore low-pause di eliminazione identificazioni simultanee. [AVANTI] Utilizzando il collettore low-pause di eliminazione identificazioni simultanee, per impostazione predefinita JVM dimensiona automaticamente gli spazi della nuova generazione e, come già spiegato, con il JDK versione 5.0 e precedenti, JVM tenterà di promuovere anzitempo gli oggetti nel tentativo di ridurre la durata delle pause causate dalle garbage collection minori. Le impostazioni automatiche di JVM subiscono interferenze se l'utente specifica in modo esplicito il valore delle dimensioni massime e minime dell'heap, o se specifica in modo esplicito un tasso di sopravvivenza o una soglia di occupazione. [AVANTI] Ora che abbiamo visto in dettaglio il funzionamento dei garbage collector generazionali e dei vari criteri di garbage collection, nonché le modalità di impostazione delle relative impostazioni predefinite, possiamo introdurre il concetto di prestazioni della garbage collection: quando è consigliabile utilizzare un determinato criterio di garbage collection piuttosto che un altro? Quali sono le linee guida generali che garantiscono buone prestazioni di garbage collection all'interno dell'applicazione? Inoltre, parleremo brevemente degli strumenti di analisi delle prestazioni Java disponibili gratuitamente su HP-UX. Anche grazie alle vostre nuove conoscenze dei meccanismi di funzionamento del garbage collector, potrete ora apprezzare maggiormente gli strumenti messi a vostra disposizione, che vi forniranno informazioni preziose per individuare i colli di bottiglia nelle prestazioni dell'applicazione da voi sviluppata. Consiglio vivamente di attivare i profili JVM e utilizzare gli strumenti per analizzare sia l'applicazione, sia la Java Virtual Machine, allo scopo di individuare i problemi e stabilire come risolverli. [AVANTI] Incomincerò dandovi le linee guida generali per un corretto dimensionamento del Java heap. Lo scopo è quello di ridurre al minimo il numero di garbage collection complete e relativi costi. Assicuratevi che gli oggetti dalla vita breve non vengano occupati prima del tempo innalzando la soglia di occupazione, e controllate altresì che le dimensioni della nuova generazione siano corrette.
Infatti, una nuova generazione troppo estesa comporterebbe lunghe pause, a causa dell'abnorme durata delle garbage collection minori, mentre una nuova generazione troppo piccola potrebbe occupare troppo rapidamente l'area vecchia, causando una frequenza eccessiva di garbage collection complete, con i costi che ne conseguono. [AVANTI] Accertatevi inoltre di evitare le garbage collection complete superflue, e relativi costi, dovute a chiamate specifiche a System.gc() o Runtime.gc() dall'applicazione. Specificate l'opzione –XX:+DisableExplicitGC nella riga di comando Java per comunicare a JVM di ignorare ogni eventuale chiamata a queste due routine esplicite di garbage collection. Anche in questo caso potete utilizzare gli strumenti a vostra disposizione per vedere se l'applicazione sta chiamando queste routine. Infine, se l'applicazione utilizza il protocollo RMI, assicuratevi di impostare l'intervallo di garbage collection del server come del client sul valore massimo, come illustrato in questa diapositiva. In caso contrario, verrà attivata una garbage collection completa ogni 60 secondi. [AVANTI] Utilizzate il criterio di garbage collection più adatto in base al tipo di applicazione. Nel caso di un'applicazione di piccole dimensioni in esecuzione su una scatola piccola, è consigliabile utilizzare un garbage collector seriale a thread singolo. Invece, nel caso di un'applicazione di grandi dimensioni orientata alla velocità in esecuzione su un sistema dotato di più di 2 processori, consiglio di ricorrere a un garbage collector parallelo ad alta velocità; inoltre, se la vostra applicazione risente in modo particolare delle pause di garbage collection, utilizzate un garbage collector eliminazione identificazioni simultanee. Durante l'esecuzione con Hyper-Threading Montecito attivato, attualmente disponibile con la versione 11i v3, assicuratevi di impostare manualmente un numero di thread di garbage collection paralleli equivalente al numero di core fisici presenti nella scatola; assicuratevi inoltre di disattivare in modo esplicito il binding dei thread di garbage collection specificando l'opzione –XX:-BindGCTaskThreadsToCPUs nella riga di comando Java. [AVANTI] Se non sapete con certezza cosa sta facendo l'applicazione o se non sapete esattamente quale valore sincronizzare in seguito, scaricate ed eseguite i nostri strumenti di analisi delle prestazioni. [AVANTI] Questi strumenti vi forniranno all'istante la risposta a domande fondamentali. Ad esempio: Cosa stanno facendo i vari thread dell'applicazione? Quali thread vengono eseguiti lentamente? In che punto passa più tempo ogni thread? Il problema rilevato è causato dal garbage collector o da qualcos'altro? [AVANTI] Una volta identificato il problema sul macro livello, grazie agli strumenti potrete scandagliare l'applicazione alla
ricerca del motivo preciso del rallentamento. [AVANTI] E anche per oggi siamo arrivati alla fine della nostra presentazione, ma prima di concludere facciamo un riepilogo dei punti principali: Punto numero 1: assicuratevi di aver compreso a fondo il funzionamento di ogni criterio di garbage collection. Punto numero 2: cercate di comprendere le impostazioni predefinite di ciascun criterio. Punto numero 3: utilizzate il criterio di garbage collection più adatto al carico di lavoro in questione. Punto numero 4: seguite le linee guida di sincronizzazione generali. Infine, se l'applicazione non fornisce prestazioni ottimali, utilizzate gli strumenti forniti per individuare e risolvere rapidamente qualsiasi problema di prestazioni. E con questo la presentazione di oggi è conclusa. Per ulteriori informazioni www.hp.com/go/kod © 2008 Hewlett-Packard Development Company, L.P. Le informazioni contenute in questo documento sono soggette a modifica senza preavviso. Le sole garanzie a copertura dei prodotti e dei servizi HP sono riportate nelle esplicite dichiarazioni di garanzia che accompagnano tali prodotti e servizi. Nulla di quanto qui contenuto potrà essere interpretato come un elemento atto a costituire una garanzia supplementare. HP declina qualsivoglia responsabilità per errori od omissioni di natura tecnica o editoriale qui contenuti.
Puoi anche leggere