2009-12-23 12 views
30

Ho fatto molte ricerche e conosco un sacco di altre persone si verificano gli stessi problemi di memoria OOM con BitmapFactory. La mia app mostra solo una memoria totale disponibile di 4 MB utilizzando Runtime.getRuntime ().totalMemory(). Se il limite è 16 MB, allora perché la memoria totale non cresce per lasciare spazio alla bitmap? Invece genera un errore.BitmapFactory OOM mi fa impazzire

anche io non capisco che se ho 1.6MB di memoria libera secondo a Runtime.getRuntime().freeMemory() perché ottengo un errore che dice "VM non ci permetterà di allocare 614400 byte"? Sembra che io abbia un sacco di memoria disponibile .

La mia app è completa tranne questo problema, che scompare quando I riavvia il telefono in modo che la mia app sia l'unica cosa in esecuzione. Sto usando un HTC Hero per il test del dispositivo (Android 1.5).

A questo punto, sto pensando che l'unico modo per aggirare questo è in qualche modo evitare di utilizzare BitmapFactory.

Chiunque ha qualche idea su questo o una spiegazione sul motivo per cui VM non sarà allocare 614 KB quando c'è 1,6 MB di memoria libera?

+0

[questo] (http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue-while-loading-an-image-to-a-mapmap-object/14731953#14731953) posso aiutare! –

risposta

2

1.6 MB di memoria sembra molto ma potrebbe essere il caso che la memoria sia così frammentata da non poter allocare un blocco così grande di memoria in una volta sola (tuttavia questo suona molto strano).

Una causa comune di OOM durante l'utilizzo delle risorse di immagine è quando si esegue la decompressione di immagini JPG, PNG, GIF con risoluzioni veramente elevate. Devi tenere a mente che tutti questi formati sono abbastanza compressi e occupano pochissimo spazio ma una volta caricate le immagini sul telefono, la memoria che useranno è qualcosa come width * height * 4 bytes. Inoltre, quando la decompressione entra in gioco, alcune altre strutture di dati ausiliari devono essere caricate per la fase di decodifica.

45

[Si noti che (come illustrato da CommonsWare di seguito) l'intero approccio in questa risposta si applica solo al 2.3.x (Gingerbread). A partire da Honeycomb I dati bitmap vengono allocati nell'heap della VM.]

I dati bitmap non sono allocati nell'heap della VM. C'è un riferimento ad esso nell'heap della VM (che è piccolo), ma i dati reali sono allocati nell'heap nativo dalla libreria grafica Skia sottostante.

Sfortunatamente, mentre la definizione di BitmapFactory.decode ...() dice che restituisce null se i dati dell'immagine non possono essere decodificati, l'implementazione di Skia (o piuttosto la colla JNI tra il codice Java e Skia) registra il messaggio che stai vedendo ("VM non ci permetterà di allocare byte xxxx") e poi lancia un'eccezione OutOfMemory con il messaggio fuorviante "dimensione bitmap supera il budget VM".

Il problema non si trova nell'heap della VM ma piuttosto nell'heap nativo. L'heap nativo è condiviso tra le applicazioni in esecuzione, quindi la quantità di spazio libero dipende da quali altre applicazioni sono in esecuzione e dal loro utilizzo di bitmap. Ma, dato che BitmapFactory non tornerà, hai bisogno di un modo per capire se la chiamata avrà successo prima di farcela.

Ci sono routine per monitorare la dimensione dell'heap nativo (vedere i metodi getNative della classe Debug). Tuttavia, ho trovato che getNativeHeapFreeSize() e getNativeHeapSize() non sono affidabili. Quindi in una delle mie applicazioni che crea dinamicamente un gran numero di bitmap faccio quanto segue.

La dimensione dell'heap nativo varia in base alla piattaforma.Quindi all'avvio, controlliamo la dimensione massima dell'heap della VM consentita per determinare la dimensione massima consentita dell'heap nativo. [I numeri magici sono stati determinati dai test su 2.1 e 2.2, e possono essere diversi su altri livelli API.]

long mMaxVmHeap  = Runtime.getRuntime().maxMemory()/1024; 
long mMaxNativeHeap = 16*1024; 
if (mMaxVmHeap == 16*1024) 
    mMaxNativeHeap = 16*1024; 
else if (mMaxVmHeap == 24*1024) 
    mMaxNativeHeap = 24*1024; 
else 
    Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap); 

Poi ogni volta che abbiamo bisogno di chiamare BitmapFactory che precedono la chiamata da un controllo della forma.

long sizeReqd  = bitmapWidth * bitmapHeight * targetBpp/8; 
long allocNativeHeap = Debug.getNativeHeapAllocatedSize(); 
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap) 
{ 
    // Do not call BitmapFactory… 
} 

Nota che il heapPad è un numero magico per tener conto del fatto che: a) la segnalazione di dimensione heap nativo è "soft" e b) vogliamo lasciare un po 'di spazio nel mucchio nativo per altre applicazioni. Al momento stiamo lavorando con un pad 3 * 1024 * 1024 (cioè 3Mbytes).

+0

Un approccio molto interessante e pratico. – BonanzaDriver

+0

Post interessante. Hai qualche prova/fonte per questo ?: "L'heap nativo è condiviso tra le applicazioni in esecuzione, quindi la quantità di spazio libero dipende da quali altre applicazioni sono in esecuzione e dal loro utilizzo bitmap." – mibollma

+0

Questo era basato sulla lettura dell'origine di Skia e in particolare su come gestisce bitmap.recycle(). Ma in retrospettiva, mentre non vedo alcun riferimento/gestione per applicazione in quel codice (che è quello che mi ha portato a quella dichiarazione), ora penso che sia troppo forte/sbagliato.L'heap nativo verrà assegnato per applicazione, basandosi sulla normale gestione della memoria per processo della VM e pertanto il codice Skia non deve preoccuparsi dei problemi relativi alle applicazioni/processi. – Torid

0

Anche se in genere non ha senso rilevare un errore perché solitamente vengono lanciati solo dal vm ma in questo caso particolare l'errore viene generato dal codice jni colla quindi è molto semplice gestire casi in cui non è possibile caricare l'immagine: cattura l'OutOfMemoryError.

0

Sebbene si tratti di una risposta di livello abbastanza elevato, il problema per me si è rivelato l'utilizzo dell'accelerazione hardware su tutte le visualizzazioni. La maggior parte delle mie visualizzazioni ha una manipolazione personalizzata di Bitmap, che ho ritenuto essere l'origine della grande dimensione nativa dell'heap, ma in effetti disabilitando l'accelerazione hardware l'utilizzo dell'heap nativo è stato ridotto di un fattore 4.

Sembra come sebbene l'accelerazione hardware esegua tutti i tipi di memorizzazione nella cache delle viste, creando bitmap e poiché tutte le bitmap condividono l'heap nativo, la dimensione dell'allocazione può aumentare in modo considerevole.

+0

E come lo facciamo? ... –

+1

@Henrique Sousa: aggiungi android: hardwareAccelerated = "false" alla tua attività in AndroidManifest.xml – samgak

+0

Incredibilmente questo ha risolto il problema .. Non posso credere quanto poco Google abbia documentato questo quadruplicato utilizzo della memoria ! –

2

Sembra che i problemi indicati in Torid's answer siano stati risolti nelle versioni più recenti di Android.

Tuttavia, se si utilizza una cache di immagini (una specializzata o anche solo una normale HashMap), è piuttosto facile ottenere questo errore creando una perdita di memoria.

Nella mia esperienza, se inavvertitamente mantenere il vostro Bitmap riferimenti e creare una perdita di memoria, errore di OP (un riferimento ai metodi BitmapFactory e nativi) è quella che andrà in crash la vostra applicazione (fino a ICS - 14 e +?)

Per evitare questo, rendere il vostro "lascia andare" delle vostre bitmap. Ciò significa utilizzare SoftReferences nell'ultimo livello della cache, in modo che Bitmap possa ricavarne i dati inutili. Questo dovrebbe funzionare, ma se si verificano ancora arresti anomali, puoi provare a contrassegnare esplicitamente alcune bitmap per la raccolta utilizzando bitmap.recycle(), ricorda di non restituire mai una bitmap da utilizzare nella tua app se bitmap.isRecycled().

Per inciso, LinkedHashMaps sono un ottimo strumento per una facile implementazione abbastanza buone strutture di cache, soprattutto se si combinano i riferimenti duri e molli come in this example (starting line 308) ... ma utilizzando riferimenti duri è anche come è possibile ottenere da soli in memoria situazioni di perdita se si incasina.

Problemi correlati