2011-10-14 44 views
13

我看到一個非常奇怪的問題。實際上,即使顯然存在大量內存,有時大型位圖內存分配也會失敗。有一些帖子似乎提出了類似的問題,但它們都與pre-honeycomb android相關。我的理解是圖像現在分配在堆上,而不是外部存儲器中。無論如何,請看下面這個日誌:OutOfMemory錯誤雖然有空閒內存可用

10-14 13:43:53.020: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 40.637MB for 942134-byte allocation 
    10-14 13:43:53.070: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 126K, 11% free 41399K/46343K, paused 31ms 
    10-14 13:43:53.130: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 920K, 13% free 40478K/46343K, paused 30ms 
    10-14 13:43:53.180: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1026K, 13% free 40479K/46343K, paused 30ms 
    10-14 13:43:53.250: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 931K, 12% free 41193K/46343K, paused 31ms 
    10-14 13:43:53.250: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 41.313MB for 1048592-byte allocation 
    10-14 13:43:53.280: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 11% free 42217K/47431K, paused 31ms 
    10-14 13:44:01.520: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 3493K, 15% free 40646K/47431K, paused 3ms+9ms 
    10-14 13:44:08.130: DEBUG/dalvikvm(31533): GC_EXPLICIT freed 16461K, 47% free 25527K/47431K, paused 3ms+6ms 
    10-14 13:44:09.150: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1007K, 45% free 26191K/47431K, paused 35ms 
    10-14 13:44:09.160: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 29.334MB for 3850256-byte allocation 
    10-14 13:44:09.200: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 0K, 37% free 29951K/47431K, paused 2ms+4ms 
    10-14 13:44:11.970: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1878K, 38% free 29784K/47431K, paused 37ms 
    10-14 13:44:12.410: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 62K, 36% free 30441K/47431K, paused 32ms 
    10-14 13:44:12.440: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 32% free 32325K/47431K, paused 32ms 
    10-14 13:44:12.440: INFO/dalvikvm-heap(31533): Forcing collection of SoftReferences for 3850256-byte allocation 
    10-14 13:44:12.480: DEBUG/dalvikvm(31533): GC_BEFORE_OOM freed 124K, 33% free 32200K/47431K, paused 37ms 
    10-14 13:44:12.480: ERROR/dalvikvm-heap(31533): Out of memory on a 3850256-byte allocation. 

我很抱歉包括這麼多的日誌記錄,我希望它是相關的。我讀到的方式是系統不斷調整堆大小,直到最終達到堆最大值。然後,我們請求一個特別大的分配失敗。顯然有足夠的內存可用(大約15 megs)。這是否意味着堆在內部是分散的,並且沒有足夠大的連續內存段來處理我們的分配?如果是這種情況,我該怎麼辦?如果那不是,那麼是什麼?

在此先感謝。

+0

您是否在使用softreferences? – Raunak

+0

不是直接的,雖然第3行說了一些關於SoftReferences的內容。就我所知,我使用基於Google收藏的緩存,它使用WeakReferences。 – EightyEight

+0

我在OS 4.1.2上的S3上看到了同樣的問題,顯然有更多可用內存: 10-02 14:28:22.458 5333 9044 D dalvikvm:GC_BEFORE_OOM已釋放0K,54%免費30274K/65543K,暫停120毫秒,總共120毫秒 –

回答

16

奇怪的行爲是因爲位圖被分配在本地堆而不是垃圾收集,但android只能跟蹤垃圾收集堆上的對象。如果您進行堆轉儲,則可以從Android 2.2(或者可能是2.3)中更改並分配位圖。

回到問題,您的問題很可能是您手動加載的位圖不適當地釋放。一個典型的問題是,一些回調仍然設置或視圖仍然指向位圖。 另一個常見的問題是,如果你手動加載大的位圖(不是資源),當你不再需要它時,你需要在它們上調用recycle(),這將從本地內存中釋放位圖,垃圾收集器將能夠按照它的工作進行工作。 (GC只能看到GC堆上的對象,並且沒有任何對象可以釋放內存以從本地堆中釋放內存,實際上甚至不關心它。)

我有這個小寶寶在手所有的時間:

public static void stripImageView(ImageView view) { 
    if (view.getDrawable() instanceof BitmapDrawable) { 
     ((BitmapDrawable)view.getDrawable()).getBitmap().recycle(); 
    } 
    view.getDrawable().setCallback(null); 
    view.setImageDrawable(null); 
    view.getResources().flushLayoutCache(); 
    view.destroyDrawingCache(); 
} 
+0

我不確定蜂窩hege是否正確。我相信圖像分配在應用程序堆上 - 基於此Google IO 2011演講(幻燈片18 http://static.googleusercontent.com/external_content/untrusted_dlcp/www.google.com/en//events/io/2011/靜態/ presofiles/memory_management_for_android_apps.pdf)。有趣的是,當我分配一個大的位圖時,我看到了我的應用程序堆增長,並且當我釋放時縮小(代碼類似於您發佈的內容)> – EightyEight

+1

Yap,我告訴過你了:)「從Android 2.2 )這已經改變了......「 – hege

+1

不幸的是,這似乎並不足夠,不知道爲什麼。回收/ etc似乎沒有影響堆的大小(至少在這個4.0設備上)。 – 2013-03-28 02:51:48

2

的圖像是取出從Web,每個範圍從300K到500K在 大小,並且被存儲在可繪的ArrayList。

從網絡加載的圖像的kb文件大小並不直接相關。由於它們被轉換爲位圖,因此您需要計算普通ARGB圖像的每個圖像的寬*高* 4字節。 (px中的寬度和高度)。

位圖消耗本機堆,它通常不會顯示在hprof中。 hprof應該只顯示對象的數量,即剩下的BitmapDrawables或Bitmap。

我使用此代碼在我的應用程序輸出的應用程序和本地堆使用的當前使用的內存:

public static void logHeap(Class clazz) { 
    Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576)); 
    Double available = new Double(Debug.getNativeHeapSize())/1048576.0); 
    Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0); 
    DecimalFormat df = new DecimalFormat(); 
    df.setMaximumFractionDigits(2); 
    df.setMinimumFractionDigits(2); 

    Log.d(APP, "debug. ================================="); 
    Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]"); 
    Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)"); 
    System.gc(); 
    System.gc(); 

    // don't need to add the following lines, it's just an app specific handling in my app   
    if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) { 
     android.os.Process.killProcess(android.os.Process.myPid()); 
    } 
} 

我開始或在開發過程中完成一個活動時調用。

logHeap(this.getClass()); 

下面是一些信息性鏈接 - 通常有很多關於此主題的線程在這裏。

這裏也是由羅曼蓋伊(Android框架工程師)關於軟引用,弱引用,簡單的緩存,圖像處理有用的幻燈片:http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

+0

謝謝你的回答Mehul。不幸的是,您的所有資源都早於Honeycomb。我會檢查你的源碼cod.e – EightyEight

0

是的當我們使用大尺寸的位圖時,出現「內存不足」錯誤。因爲我有一個解決方案,解碼的圖像,我們可以很容易地避免這個錯誤。

BitmapFactory.Options o = new BitmapFactory.Options(); 
o.inJustDecodeBounds = true; 
FileInputStream fis = new FileInputStream(files.getAbsolutePath()); 
BitmapFactory.decodeStream(fis, null, o); 
fis.close(); 
final int REQUIRED_SIZE=70; 
int width_tmp=o.outWidth, height_tmp=o.outHeight; 
int scale=1; 
while(true){ 
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) 
break; 
width_tmp/=2; 
height_tmp/=2; 
scale*=2; 
} 
BitmapFactory.Options op = new BitmapFactory.Options(); 
op.inSampleSize = scale; 
fis = new FileInputStream(files.getAbsolutePath()); 
bitmap[i] = BitmapFactory.decodeStream(fis, null, op); 
fis.close(); 
+1

因此,當你獲得一個OOM時,你要重新調整圖像大小?我想這是一個潛在的解決方案,但它不能回答我原來的問題。 – EightyEight

0

系統不調整堆大小。你的應用程序有一個(半)預定義的堆空間。堆大小可以在一些自定義ROM中進行調整。儘管如此,這與你的問題並不緊密。

你看到的垃圾收集的順序似乎表明,在GC_BEFORE_OOM發生時,你的應用程序的堆空間不足了。 Dalvik記錄器報告的統計數據可能是全系統的。

+1

括號中的數字是進程ID,所以報告的統計數據是每個進程,而不是系統範圍。我試圖解決這種不協調。 Dalvik報告可用內存的30%,下一行表示內存已耗盡。離奇。 – EightyEight