HP-UX 11i Conoscenza on demand: best practice per l'ottimizzazione delle prestazioni direttamente dai nostri laboratori

Pagina creata da Sara Filippini
 
CONTINUA A LEGGERE
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