2012-12-31 2 views
5

Sto scrivendo alcuni software "big data" che devono contenere molti dati in memoria. Ho scritto un prototipo in C++ che funziona alla grande. Tuttavia, gli utenti finali in genere codificano in Java, quindi mi hanno chiesto di scrivere anche un prototipo java.ottimizzazione della dimensione in memoria java

Ho fatto una lettura di fondo su memoria-footprint in java e alcuni test preliminari. Ad esempio, Diciamo che ho questo oggetto

public class DataPoint{ 

    int cents, time, product_id, store_id; 

    public DataPoint(int cents, int time, int product_id, int store_id){ 
    this.cents = cents; 
    this.time = time; 
    this.product_id = product_id; 
    this.store_id = store_id; 
    } 
} 

In C++ la sizeof questa struttura è 16 byte, che ha un senso. In Java dobbiamo essere indiretti. Se creo, ad esempio, 10 m di questi oggetti e utilizzo Runtime.totalMemory() - Runtime.freeMemory() prima di e dopo e poi divido a seconda dei casi, ottengo circa 36 byte per struttura. Una differenza di memoria di ~ 2.4 volte è piuttosto sgradevole; diventerà brutto quando proveremo a tenere in memoria centinaia di milioni di DataPoints.

Ho letto da qualche parte che in casi come questo in Java è meglio archiviare i dati come array - essenzialmente un archivio basato su colonne piuttosto che un archivio basato su righe. Penso di capirlo: la modalità basata su colonne riduce il numero di riferimenti e forse la JVM può persino impacchettare intelligentemente le intere in parole da 8 byte.

Quali altri trucchi posso utilizzare per ridurre l'impronta di memoria di ciò che è essenzialmente un blocco di memoria che ha una dimensione molto grande (milioni/miliardi di punti dati) e una dimensione molto piccola (il numero O (1) di colonne/variabili)?

Risulta archiviare i dati come 4 int array utilizzati esattamente 16 byte per voce. La lezione: piccoli oggetti hanno un overhead proporzionale sgradevole in java.

+3

totalMemory include anche la memoria libera, prova a misurare con totalMemory() - freeMemory() – Henry

+0

Prima di tutto se si desidera una figura veramente accurata, fare un dump dell'heap dell'applicazione e aprire tale dumping con qualcosa come Memory Analyzer (http : //www.eclipse.org/mat/) per ottenere una cifra accurata. 2nd - qual è il modello di accesso a questi dati? forse puoi guadagnare più headroom usando una libreria cache in grado di distribuire porzioni inutilizzate su disco? dì dividi i tuoi punti in matrici di oggetti da 10K e archivia questi "blocchi" in infinispan (http://www.jboss.org/infinispan/) o simili? – radai

+0

@Henry Buon punto! Misurare con totalMemory() - freeMemory() dà un valore di 36 byte per struttura. Meglio di 52, ma ancora> 2x quello di C++. – andyInCambridge

risposta

2

Non è così semplice vedere quanta memoria la struttura dei dati assume in Java. totalMemory() mostra lo spazio allocato per vm che è maggiore dell'utilizzo effettivo. Puoi provare a utilizzare il profiler Java che mostra il consumo di spazio delle tue strutture dati, sono abbastanza facili da configurare ed eseguire. Uno strumento gratuito a portata di mano è lo VisualVM di Java che, ad esempio, mostra il comportamento della memoria dell'applicazione, inoltre imparerai un po 'come funziona il GC di Java se lo usi.

VisualVM screenshot che mostra le prestazioni impronta (immagine da http://visualvm.java.net/features.html): enter image description here

Si dovrebbe anche considerare la finale le variabili se è possibile. Permette alle macchine virtuali Java di ottimizzare meglio il bit del codice (non sono sicuro se lo spazio lo consente).

+0

Rendendoli definitivi è una buona idea. L'ho appena testato, tuttavia non cambia l'impronta della memoria. – andyInCambridge

0

Prima di tutto un oggetto in Java sarà sempre un po 'più grande di una versione C++ poiché l'oggetto incapsula rtti che consente di fare instanceof ecc che non è possibile in C++. Inoltre facilita la gestione della memoria che dovresti fare manualmente, quindi puoi anche considerare questa parte del tuo codice C++ come non parte della base del codice.

È possibile esaminare Flyweight Pattern per ridurre i requisiti di memoria in modo da riutilizzare lo DataPoints (rendere la classe Immutable). Suppongo che se hai miliardi di punti come dici, alcuni saranno probabilmente gli stessi valori.
Sono sicuro che altri qui forniranno ulteriori informazioni sull'ottimizzazione nello spazio di memoria

0

A seconda degli intervalli di valori, è possibile utilizzare tipi di dati più piccoli. Puoi farcela usando byte o short per alcuni membri?