2012-05-21 14 views
19

Sto testando su android 3.1, un'opzione heapsize grande, circa 250M di memoria disponibile.Errore di frammentazione della memoria GC per Android. Soluzione?

ho impostato il seguente codice di essere eseguito ogni volta che mi ha colpito un pulsante Test preferenze di mia app:

float [][][]foo = new float[3][2048][2048]; 
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888); 
bm.recycle(); 
bm = null; 
foo = null; 

Ho un sacco di memoria per questo - posso colpire il pulsante di un paio di volte senza problemi.

Ma se continuo a premere il pulsante, alla fine (meno di 20 colpi) muore con OutOfMemory. [Solitamente in android.graphics.Bitmap.nativeCreate (metodo nativo)]

Non succede nient'altro: non devo mai lasciare PreferenzeAttività. C'è un piccolo Toast che viene visualizzato anche quando premo il pulsante, quindi è in corso una piccola quantità di altre attività dell'interfaccia utente.

Ciò è dovuto alla frammentazione o solo a un orribile errore nel codice Bitmap di Android e/o GC? O sto facendo qualcosa di stupido? (Si prega di lasciare che sia qualcosa di stupido ...)

Qualcuno ha una soluzione? Perché quanto sopra è abbastanza rappresentativo di ciò che il mio codice deve fare ogni volta che l'utente lo invoca, e in questo momento, nonostante una meticolosa cancellazione delle variabili, muore dopo alcuni usi. (E questo è stato me alla guida di noci per molto tempo!)

[Update]

Ho confermato che è un problema di frammentazione o GC bug, come un heap dump mostra che sto usando solo 5.6M quando inattivo (senza perdite) con un picco di circa 26 M durante l'elaborazione. (Inoltre, l'heap nativo rimane inferiore a 4M.) Mentre l'heap java nel frattempo cresce fino al limite di 280M sul mio dispositivo di test, a quel punto inizio a ricevere le eccezioni OutOfMemory. Quindi sto usando solo il 10% del mio heap disponibile al massimo, ma ricevo OutOfMemory.

[L'aggiunta di una chiamata a System.gc() purtroppo risolve il semplice caso di test che ho fornito sopra. Dico sfortunato perché (A) non dovrebbe fare la differenza, e (B) perché lo chiamo già regolarmente nel mio codice reale quindi significa che il mio semplice caso di test sopra è troppo semplice.]

Qualcun altro ha eseguito in questo? Qualche soluzione alternativa? C'è un modo elegante per riavviare la mia app?

[Update]

La seguente versione provoca affidabile OutOfMemory in 3 o 4 invocazioni (pressioni del tasto):

float [][][]foo = new float[3][2048][2048]; 
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888); 
int []bar = new int[3*2048*2048]; 
bm.recycle(); 
bm = null; 
System.gc(); 
foo = null; 
System.gc(); 
bar = null; 
System.gc(); 

tracing memoria mostra il cumulo in costante crescita ogni chiamata fino a raggiungere il limite e muore. Se rimuovo una delle tre allocazioni, raggiunge l'equilibrio e sopravvive indefinitamente. Rimuovendo tutto tranne l'ultimo gc() provoca la sua morte un po 'prima.

Direi che questo è un problema di frammentazione, non un bug gc di per sé. Se qualcuno sa come risolverlo, fammi sapere. L'allocazione int [] è per la scrittura di un Bitmap, quindi non ho la possibilità di allocarlo come array 2d (limitazione della libreria di Bitmap di Android).

+0

Non so se questo sarà di aiuto, ma dalvik usa un [Mark-and-Sweep] (http://www.brpreiss.com/books/opus5/html/page424.html#SECTION0014300000000000000000) tipo GC. Dove cercherà di aspettare fino a quando tutta la memoria disponibile viene utilizzata, quindi ripulire tutto in una volta (problemi di prestazioni, perché ha bisogno di interrompere l'elaborazione in GC). Forse la chiamata 'System.gc();' è esattamente ciò che è necessario con questo problema, perché il GC si aspetta oggetti più piccoli. – Ancantus

+1

@Ancantus - Sfortunatamente, come detto, chiamo System.gc() dopo ogni recycle()/null nel mio codice reale e ancora ottengo questo comportamento. Sto pensando che si tratti di un problema di frammentazione o qualcosa di simile a System.gc() che viene ignorato o si comporta in modo diverso nei thread in background. Sarei disposto a uccidere e riavviare la mia app da zero per compattare la memoria se ci fosse un modo affidabile per farlo. Mi piacerebbe anche una chiamata per dirmi, post-GC, quanto dell'heap java è lo spazio libero (che mi direbbe immediatamente e di sicuro se si tratta di un problema di frammentazione o di una perdita oscura). Ce n'è uno? – Brandyn

+0

Yah come probabilmente sapete chiamare 'System.gc();' indica solo il GC per iniziare, non significa necessariamente che verrà eseguito. In realtà stavo facendo una ricerca in giro per problemi relativi alla bitmap GC, [questa domanda SO] (http://stackoverflow.com/questions/7852943/what-does-bitmaprecycle-in-android-honeycomb-actually-do) potrebbe aiutare. – Ancantus

risposta

1

Per impedire la frammentazione, è sufficiente allocare una volta l'array grande AND la bitmap e riutilizzarla.

Per Android, ci sono alcuni accorgimenti a questo, come Android tenta di gestire le risorse della vostra App in una certa misura.Ad esempio, gli Activity o View possono essere scaricati se non visibili e rieseguirli più tardi se diventano nuovamente visibili. Quindi le cose grandi dovrebbero essere meglio conservate da un oggetto Application o da un posto static.

Se questo è utilizzato solo per alcune finestre di dialogo delle preferenze, è necessario riservarlo al primo utilizzo, ma tenerlo in seguito, per non utilizzare tutta quella memoria ad ogni esecuzione. Se viene usato raramente, forse è necessario riavviare l'applicazione dopo aver lasciato la schermata delle preferenze. L'utente non ha bisogno di prenderne nota se è fatto bene, e si otterrà di nuovo un processo nuovo e amichevole.

+0

Purtroppo non un'opzione, si tratta di un'applicazione per l'elaborazione di immagini che prende immagini fornite dall'utente di varie dimensioni (senza hard-cap max - max è qualsiasi cosa può spremere nella memoria disponibile) – Brandyn

+0

Anche senza array statico e Bitnaos puoi ancora provare a riavviare l'applicazione dopo una o alcune immagini, forse è sufficiente chiudere l'ultima 'Activity' di' finish() 'e riaprirla tramite' startActivity'. Tuttavia la VM può essere riutilizzata in un 'intelligente 'modo, quindi potrebbe essere necessario eseguire un servizio di supporto in uscita, che riavvierebbe di nuovo l'attività principale' – dronus

+0

@Brandyn Nel 2016 il problema con la frammentazione della memoria è ancora presente. Ho iniziato a utilizzare LruCache ma dopo un paio di mesi di utilizzo vedo che causa OutOfMemory a causa della frammentazione della memoria.Vedi https://github.com/andstatus/andstatus/issues/351 L'unica soluzione che voglio provare ora è allocare una cache di immagini una volta e poi riutilizzare il bitma memorizzato nella cache memoria ps. Dovrò allocare bitmap di dimensioni massime per poter essere riusabili ... – yvolk

4

Ecco un altro SO pagina che apparentemente ha una soluzione per questo problema:

Strange out of memory issue while loading an image to a Bitmap object

In particolare, la risposta da Ephraim (tratto):

"1) Ogni volta che fate BitmapFactory. decodificareXYZ(), assicurarsi di passare in un BitmapFactory.Options con inPurgeable impostato su true (e preferibilmente con inInputShareable impostato su true.)

"2) NON utilizzare MAI Bitmap.createBitmap (larghezza, altezza, Config.A RGB_8888). Voglio dire MAI! Non ho mai avuto quella cosa non aumentare l'errore di memoria dopo pochi passaggi. Nessuna quantità di recycle(), System.gc(), qualunque sia l'aiuto. Ha sempre sollevato un'eccezione. L'altro modo in cui funziona è quello di avere un'immagine fittizia nei tuoi drawable (o un'altra Bitmap che hai decodificato usando il punto 1 sopra), ridimensionarla in base a ciò che vuoi, quindi manipolare la Bitmap risultante (come passarla su una Canvas per più divertimento). Quindi, cosa invece dovresti usare è: Bitmap.createScaledBitmap (srcBitmap, width, height, false). Se per qualsiasi motivo è necessario utilizzare la forza bruta creare metodo, allora almeno passare Config.ARGB_4444."

Nei commenti, alcune persone hanno detto che questo ha risolto il loro problema, che è molto simile a quella del PO qui.

Aggiungo che Diane Hackborn ha commentato che a partire dal 3.0 Android non assegna più bitmap dall'heap nativo ma invece li alloca direttamente dall'heap normale.Questo potrebbe rendere irrilevanti i dati dell'heap nativo. Vedi il commento di hackbod in questa pagina:

Bitmaps in Android

immagino ° implica un cambiamento abbastanza importante come quello di Honeycomb per quanto riguarda l'allocazione di bitmap, e quindi questo potrebbe spiegare perché ci sono bug con tali allocazioni (se ci sono). Non so quale effetto abbia questo cambiamento sul comando recycle(), ma alla luce dei commenti sopra di Ephraim, la risposta potrebbe essere "non molto buona".

Infine,

Per utilizzare largeHeap di ingerire enormi bitmap potrebbe essere visto come non giocare bello con altre applicazioni, soprattutto se si sta andando vicino ai limiti fisici del dispositivo. Non sono sicuro di come puoi evitarlo, ma sii preparato per un sacco di attività onPause()/onResume() mentre la tua app avanza su altre app, e fanno un passo indietro sul tuo. Questo SO risposta include una discussione di questo:

Detect application heap size in Android

+0

'inPurgeable' è ora deprecato e ignorato. – zyamys

-2

C'è un articolo molto dettagliato sulla gestione della memoria bitmap developer.android.com Android:

http://developer.android.com/training/displaying-bitmaps/manage-memory.html

Fondamentalmente si consiglia di utilizzare Bitmap.recycle() per evitare errori OutOfMemoryError per Android 2.3.3 e versioni precedenti.Secondo la documentazione libera l'oggetto nativo associato all'oggetto Bitmap.

+0

Sta usando 3.1, dove Bitmap.recycle() non ha più bisogno di essere chiamato direttamente. –

Problemi correlati