Come dimensionare l’Heap Size di un’applicazione Java

Come dimensionare l’Heap Size di un’applicazione Java

In quest'articolo vedremo come configurare e dimensionare la memoria Heap di un'applicazione Java in esecuzione su una Java Virtual Machine. La Java Virtual Machine o JVM, è la macchina virtuale che esegue i programmi scritti in bytecode, secondo questo piccolo schema: Codice Java : compilazione : bytecode : VM -> esecuzione reale del programma Il bytecode è generalmente prodotto dalla compilazione di sorgenti scritti in linguaggio Java, anche se è possibile produrre bytecode partendo da altri linguaggi; infatti, già esistono delle implementazioni, parziali o complete, di compilatori che funzionino in tal senso. Per comprendere la funzionalità della memoria Heap, bisogna prima capire come sia gestita la memoria dalla JVM, di cui lo Heap ne è parte.

Tutorial Apache POI: creiamo un file Excel
Quartz Scheduler: una libreria Open Source per schedulare processi Java
Creare un file eseguibile .exe per le applicazioni Java con Launch4j

In quest’articolo vedremo come configurare e dimensionare la memoria Heap di un’applicazione Java in esecuzione su una Java Virtual Machine.

La Java Virtual Machine o JVM, è la macchina virtuale che esegue i programmi scritti in bytecode, secondo questo piccolo schema:

Codice Java : compilazione : bytecode : VM -> esecuzione reale del programma

Il bytecode è generalmente prodotto dalla compilazione di sorgenti scritti in linguaggio Java, anche se è possibile produrre bytecode partendo da altri linguaggi; infatti, già esistono delle implementazioni, parziali o complete, di compilatori che funzionino in tal senso.

Per comprendere la funzionalità della memoria Heap, bisogna prima capire come sia gestita la memoria dalla JVM, di cui l’Heap ne è parte.

La memoria (Runtime Data Area) si suddivide essenzialmente in tre segmenti:

  • Memoria Heap: riservata allo storage degli oggetti Java;
  • Method Area: dove la JVM conserva tutte le informazioni riguardo ad una classe che sta caricando;
  • Altro: porzione di memoria dove la JVM conserva il proprio codice e le proprie strutture.
runtime data area JVM
La Runtime Data Area della JVM

Mentre la possibilità di dimensionare la Method Area sono molto limitate (in pratica si può agire solo sul parametro -XX:MaxPermSize), abbiamo invece la possibilità di modificare a piacere la dimensione dell’Heap intervenendo sui seguenti argomenti:

  • -Xmx<size>: per impostare la dimensione massima che può avere l’Heap (64 MegaByte di default);
  • -Xms<size>: per impostare la dimensione iniziale che deve avere l’Heap (circa 5 MegaByte di default);
  • -XX:MaxHeapFreeRatio<ratio>:  per impostare la percentuale massima da garantire di spazio libero all’interno dello Heap (70 di default);
  • -XX:MinHeapFreeRatio<ratio>:  per impostare la percentuale minima da garantire di spazio libero all’interno dello Heap (40 di default);

Da notare che le dimensione in Byte di default dell’Heap iniziale e massimo dipendono dalla versione di JVM e dalla architettura della macchina su cui sono installate. Un dimensionamento ottimale della memoria heap java (opzione -Xmx) ci consente inoltre di prevenire la classica e frequente eccezione di esaurimento della memoria disponibile:

java.lang.OutOfMemoryError: Java heap space

A questo punto sorge la domanda: Come dimensioniamo l’Heap Space? Quali valori diamo agli argomenti -Xmx e -Xms?

Innanzitutto è chiaro che -Xms deve essere minore o al max uguale a -Xmx (alcune guide riportano che un valore identico dei due parametri sia ottimale per evitare che l’Heap sia sottoposto a operazioni di ridimensionamento o crescita da parte del Garbage Collector, un pò come avviene per l’area di swap per i Sistemi Operativi).

In secondo luogo bisogna sapere quanto è onerosa una data applicazione Java in termine di memoria: infatti se una applicazione Java esaurisce lo spazio Heap massimo assegnato viene lanciata la classica eccezione OutOfMemoryError.

Verrebbe allora da dire di fornire quanto più memoria possibile all’applicazione Java, ma ciò porta ad un rallentamento a volte considerevole della sua esecuzione in quanto il Garbage Collector dovrà analizzare una maggiore porzione di memoria durante la sua “ricerca” di Oggetti non più referenziati (e quindi da de-allocare).

Una buona regola è invece quella di far in modo che l’Applicazione Java con il suo spazio Heap sia sempre in un intervallo tra il 40% ed il 70% dello spazio Heap massimo misurato.

Per trovare questo intervallo ottimale c’è dunque bisogno di una misurazione dello spazio massimo dell’Heap che non sempre è immediato, specie per applicazioni il cui carico sia variabile.

Se per i Sistemi Operativi a 64 bit il limite dello spazio Heap è limitato dalla memoria fisica della macchina e dagli altri applicativi che vi girano, per i sistemi a 32 bit il discorso è diverso: per sistemi Windows a 32 bit lo spazio massimo riservabile per l’Heap è 1,6 GigaByte (1611 Megabyte), per i sistemi Linux , Solaris e AIX siamo invece intorno ai 2 GigaByte. L’opzione di avvio /3GB del boot.ini per Windows (che permette di allocare 3 GigaByte di spazio per un processo) si rivela inutile per le applicazioni Java.

Per poter passare i parametri da linea di comanda basta inserire l’argomento (-Xmx o -Xms) seguito dal numero e dalla dimensione (k o K per kiloByte, m o M per MegaByte, g o G per Gigabyte, sono case insensitive), ad esempio digitiamo:

java -Xms64m -Xmx256m HelloWorld

per avviare la classe HelloWorld con 64 Megabyte riservati all’avvio e 256 totali.

Per le applicazioni compilate all’interno di Progetti Eclipse, bisogna cliccare col destro sul nome del progetto poi su Run As -> Run Configurations… e poi cliccare sul tab Arguments:

Nel pannello VM Arguments scriviamo i parametri da passare alla console

clicchiamo su Apply e poi su Run per avviare l’applicazione.

E’ possibile verificare la memoria Heap disponibile durante l’esecuzione di un programma Java utilizzando la classe Runtime, la quale ci permette di avere delle informazioni sulla memoria allocata utilizzando alcuni dei suoi metodi, per provarli scriviamo questa classe Java:

package it.appuntisoftware;

public class TestHeapSize {
  public static void main(String[]args){
    long maxHeapSize = Runtime.getRuntime().maxMemory();
    long freeHeapSize = Runtime.getRuntime().freeMemory();
    long totalHeapSize = Runtime.getRuntime().totalMemory();
    System.out.println("Max Heap Size = " + maxHeapSize+ " byte");
    System.out.println("Free Heap Size = " + freeHeapSize+ " byte");
    System.out.println("Total Heap Size = " + totalHeapSize+ " byte");
  }
}

se non specifichiamo degli argomenti, l’output sarà:

Max Heap Size = 66650112 byte
Free Heap Size = 4847304 byte
Total Heap Size = 5177344 byte

Il valore del Max Heap Size rappresenta la dimensione massima che l’Heap potrà avere per questa applicazione e di default è 64 MegaByte, il valore Free Heap Size è la memoria libera nell’Heap che ha ancora a disposizione l’applicativo, infine il valore Total Heap Size rappresenta l’Heap iniziale a disposizione della applicazione Java. A conferma delle nostre prove, possiamo provare a cambiare i valori di default passando alla JVM questi due parametri:

-Xms256m -Xmx512m

ottenendo questi valori:

Max Heap Size = 532742144 byte
Free Heap Size = 266072200 byte
Total Heap Size = 266403840 byte

L’Heap massimo è aumentato a 512 MegaByte mentre quello iniziale a 256 Megabyte, la Free Heap Size è aumentata di pari passo alla Total.

Riferimenti

Sizing the Java heap: http://publib.boulder.ibm.com/infocenter/javasdk/tools/index.jsp?topic=/com.ibm.java.doc.igaa/_1vg00014884d287-11c3fb28dae-7ff6_1001.html

The JavaTM Virtual Machine Specification: http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html

Java HotSpot VM FAQ: http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_heap_32bit

COMMENTS

WORDPRESS: 0