Sto sviluppando un'applicazione in Java eseguita su dispositivi Windows Mobile. Per raggiungere questo obiettivo, abbiamo utilizzato la JVM Esmertec JBed, che non è perfetta ma per ora ne siamo bloccati. Recentemente abbiamo ricevuto lamentele da parte dei clienti su OutOfMemoryErrors. Dopo un sacco di giochi con le cose ho scoperto che il dispositivo ha un sacco di memoria libera (circa 4 MB).Evitare la frammentazione della memoria durante l'allocazione di molti array in Java
Gli OutOfMemoryErrors si verificano sempre nello stesso punto del codice e cioè quando si espande uno StringBuffer per aggiungervi alcuni caratteri. Dopo aver aggiunto qualche logging in quest'area ho scoperto che il mio StringBuffer conteneva circa 290000 caratteri con una capacità di circa 290500. La strategia di espansione dell'array di caratteri interni è semplicemente raddoppiare le dimensioni, quindi tenterebbe di allocare una matrice di circa 580000 caratteri. Ho stampato l'utilizzo della memoria in questo periodo e ho scoperto che utilizzava circa 3,8 MB di circa 6,8 MB totali (anche se ho visto la memoria totale disponibile salire a circa 12 MB a volte, quindi c'è molto spazio per l'espansione). Quindi è a questo punto che l'applicazione riporta un OutOfMemoryError, che non ha molto senso dato quanto c'è ancora libero.
Ho iniziato a pensare al funzionamento dell'applicazione fino a questo punto. Fondamentalmente quello che sta succedendo è che sto analizzando un file XML usando MinML (un piccolo XML Sax Parser). Uno dei campi nell'XML contiene circa 300k caratteri. Il parser trasmette i dati dal disco e per impostazione predefinita carica solo 256 caratteri alla volta. Quindi, quando raggiunge il campo in questione, il parser chiamerà il metodo "characters()" del gestore più di 1000 volte. Ogni volta che creerà un nuovo carattere [] contenente 256 caratteri. Il gestore aggiunge semplicemente questi caratteri a un StringBuffer. La dimensione iniziale predefinita di StringBuffer è solo 12, così come i caratteri vengono aggiunti al buffer che dovrà crescere un certo numero di volte (ogni volta che si crea un nuovo carattere []). Il mio assunto da questo è che è possibile che mentre c'è abbastanza memoria libera dal momento che il char precedente [] s possa essere garbage collection, forse non c'è un blocco di memoria contiguo abbastanza grande da adattarsi al nuovo array che sto provando a allocare. E forse la JVM non è abbastanza intelligente da espandere la dimensione dell'heap perché è stupida e pensa che non ci sia bisogno perché apparentemente c'è abbastanza memoria libera.
Quindi la mia domanda è: qualcuno ha esperienza di questa JVM e potrebbe essere in grado di confermare in modo conclusivo o confutare le mie supposizioni sull'assegnazione della memoria? E inoltre, qualcuno ha qualche idea (supponendo che le mie supposizioni siano corrette) su come imrove l'allocazione degli array in modo che la memoria non diventi frammentata?
Nota: cose che ho provato già:
- ho aumentato la dimensione della matrice iniziale del StringBuffer e ho increaed la dimensione di lettura del parser in modo che non avrebbe bisogno di creare tanti array.
- Ho modificato la strategia di espansione di StringBuffer in modo che, una volta raggiunta una determinata soglia di dimensione, si espandesse solo del 25% anziché del 100%.
Fare entrambe le cose hanno aiutato un po ', ma come ho aumentare la dimensione dei dati XML che vanno in Ho ancora ottenere OutOfMemoryErrors in una dimensione piuttosto basso (circa. 350KB).
Un'altra cosa da aggiungere: tutto questo test è stato eseguito su un dispositivo che utilizzava la JVM in questione. Se eseguo lo stesso codice sul desktop utilizzando Java JVM SE 1.2, non ho alcun problema, o almeno non riesco a ottenere il problema finché i miei dati non raggiungono circa 4 MB di spazio.
EDIT:
un'altra cosa che ho appena provato che ha aiutato un po 'è ho impostato le Xms a 10M. Quindi questo supera il problema della JVM non espandendo l'heap quando dovrebbe e mi consente di elaborare più dati prima che si verifichi l'errore.
Sei sicuro? Questo articolo parla di come rendere gli oggetti * più facili * alla raccolta dei rifiuti. –
Non sto creando alcun oggetto di riferimento ?? Come ho già detto, non penso di avere un problema con gli oggetti che non ottengono la raccolta dei dati inutili perché la JVM segnala un sacco di memoria libera. È una questione di dov'è la memoria libera? È frammentato? È per questo che la JVM non può allocare il mio nuovo array? – DaveJohnston