2010-01-20 11 views
7

Ho scritto una libreria in C che consuma molta memoria (milioni di piccoli blocchi). Ho scritto un programma c che utilizza questa libreria. E ho scritto un programma java che usa la stessa libreria. Il programma Java è uno strato molto sottile attorno alla libreria. Fondamentalmente c'è un solo metodo nativo che viene chiamato, fa tutto il lavoro e ritorna ore dopo. Non c'è ulteriore comunicazione tra Java e la libreria nativa usando l'interfaccia di invocazione java. Né ci sono oggetti Java che consumano una notevole quantità di memoria.Perché una libreria nativa usa una quantità di memoria 1,5 volte superiore quando viene usata da java come quando viene usata da un C-Programm sotto linux?

Quindi il programma c e il programma Java sono molto simili. L'intera assegnazione di calcolo/memoriale avviene all'interno della libreria nativa. Ancora. Quando viene eseguito, il programma c consuma 3 GB di memoria. Ma il programma Java consuma 4,3 GB! (Importo VIRT riportato in alto)

Ho controllato la mappa di memoria del processo Java (utilizzando pmap). Solo 40 MB sono utilizzati dalle biblioteche. Quindi le librerie aggiuntive caricate da Java non sono la causa.

Qualcuno ha una spiegazione per questo comportamento?

MODIFICA: Grazie per le risposte finora. Per renderlo un po 'più chiaro: il codice java non fa altro che invocare la libreria nativa ONCE! L'heap java è di dimensioni standard (forse 60 MB) e non viene utilizzato (ad eccezione di una classe contenente il metodo main e l'altra classe che richiama la libreria nativa).

Il metodo di libreria nativa è di lunga durata e esegue molti mallocs e liberi. La frammentazione è una spiegazione che ho pensato anche a me stesso. Ma poiché non esiste un codice Java attivo, il comportamento della frammentazione dovrebbe essere lo stesso per il programma Java e il programma c. Dal momento che è diverso presumo anche che le implementazioni di malloc utilizzate siano diverse quando vengono eseguite in programma c o in programma Java.

+0

Osservazione intrestante. Non mi sarei aspettato questo comportamento. – x4u

+0

frammentazione di heap malloc. Vedi http://stackoverflow.com/a/28935176/166062 –

+0

@LariHotari Mentre il motivo di questa domanda era diverso (vedi la mia risposta), abbiamo avuto anche problemi con la frammentazione. L'abbiamo risolto passando a implementazioni alternative come tcmalloc, jemalloc o l'implementazione di malloc da locklessinc. –

risposta

2

Scusate ragazzi. Ipotesi errate

Mi sono abituato al 64 MB delle implementazioni Sun Java utilizzate per la dimensione massima dell'heap predefinita. Ma ho usato openjdk 1.6 per i test. Openjdk utilizza una frazione della memoria fisica se non è stata specificata la dimensione massima dell'heap. Nel mio caso un quarto. Ho usato una macchina da 4 GB. Un quarto è quindi 1GB. Lì è la differenza tra C e Java.

Purtroppo questo comportamento non è documentato da nessuna parte. L'ho trovato guardando il codice sorgente di openjdk (arguments.cpp):

// If the maximum heap size has not been set with -Xmx, 
// then set it as fraction of the size of physical memory, 
// respecting the maximum and minimum sizes of the heap. 
0

Ci sono diversi fattori che è necessario prendere in considerazione, specialmente in un linguaggio come Java, Java viene eseguito su una macchina virtuale e la garbage collection viene gestita da Java Runtime, poiché vi è uno sforzo considerevole (immagino) dall'uso la Java Invocation Interface per cambiare o eseguire il metodo nativo all'interno della libreria nativa in quanto ci dovrebbe essere un mezzo per allocare lo spazio nello stack, passare al codice nativo, eseguire il metodo nativo, tornare alla macchina virtuale Java e forse in qualche modo , lo spazio in pila non è stato liberato - questo è ciò che sarei incline a pensare.

Spero che questo aiuti, Cordiali saluti, Tom.

3

solo indovinare: si potrebbero utilizzare un'implementazione malloc non predefinito durante l'esecuzione all'interno della JVM che sintonizzarsi alle esigenze specfic della JVM e produce più in alto rispetto al general-purpose malloc nell'implementazione normale libc.

+0

Questa sarebbe la mia ipotesi. Ma non ti sto dando il +1 fino a quando non ci sono prove che sia davvero il caso. Le librerie – Omnifarious

1

Java ha bisogno di avere memoria continua per il suo heap in modo che possa allocare la dimensione massima della memoria come memoria virtuale. Tuttavia, questo non consuma memoria fisica e potrebbe anche non consumare lo scambio. Verifico di quanto aumenta la tua memoria residente.

+0

chiamate dalla JVM possono comunque liberare memoria allocata. –

+0

Una possibilità è che un thread Java sia in esecuzione in concomitanza con il codice nativo e che stiano frammentando la memoria. L'OP dice che ci sono molte piccole allocazioni. – Omnifarious

+0

La dimensione massima dell'heap per Java è impostata su 60 MB circa. Questo non può essere il motivo per consumare 1 GB di memoria in più. Ancora: la memoria residente è 3 GB, come il programma c. –

0

È difficile da dire, ma penso che il cuore del problema è che ci sono due heap nell'applicazione che devono essere mantenuti: l'heap Java standard per le allocazioni di oggetti Java (gestito dalla JVM), e l'heap C che viene gestito da chiamate a malloc/free. È difficile dire cosa sta succedendo esattamente senza vedere un codice.

0

Ecco un suggerimento per combatterlo.

Arrestare il codice C utilizzando la chiamata standard di malloc e utilizzare una versione alternativa di malloc che acquisisce memoria tramite mmap ing /dev/zero. Puoi modificare un'implementazione di malloc da una libreria o eseguire il rollover se ti senti abbastanza competente per farlo.

Ho il forte sospetto che scoprirete che il vostro problema scompare dopo averlo fatto.

Problemi correlati