2012-10-03 21 views
10

使largeHeap選項之前,我正在處理大的位圖,它的消耗幾乎可用於應用程序的整個內存,回收過來的導航和裝載工作提供輪幾乎全堆新的。但是,當某些操作需要更多內存時,應用程序會崩潰。所以我啓用largeHeap=true有更多的內存。與largeHeap位圖的循環啓用

但這樣做有一個意外的行爲,它看起來像位圖的是recycle()方法不工作的大部分時間,而在內存58MB工作(並超過有時投擲OutOfMemoryException)的應用程序現在成倍消耗內存,並保持(現在我測試的是231Mb分配內存),預期的行爲是內存管理繼續工作,應用程序不會使用超過60Mb。

我該如何避免這種情況?或有效地回收位圖?

編輯:其實,我給它提供了一個OutOfMemoryError時分配390Mb的設備上的內存。 閱讀GC_ *日誌顯示只有GC_FOR_ALLOC有時會釋放3.8Mb,但幾乎從不釋放其他GC運行。

+1

你看過這個不錯的視頻嗎? http://www.youtube.com/watch?v=_CruQY55HOk我認爲你的代碼有內存泄漏 – HitOdessit

+0

你必須告訴我們這是蜂窩前還是蜂窩 –

回答

21

你或許應該在Displaying Bitmaps Efficiently,其中包括幾種方式來有效地處理大量位圖看看,

  • 裝載大位圖高效
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 
BitmapFactory.decodeResource(getResources(), R.id.myimage, options); 
int imageHeight = options.outHeight; 
int imageWidth = options.outWidth; 

這會給你下載之前的圖像大小,並在此基礎上,你可以檢查它使用的文檔給出的解釋calculateInSampleSize()decodeSampledBitmapFromResource()您的設備和規模的大小。

計算多少,我們需要將圖像縮放,

if (imageHeight > reqHeight || imageWidth > reqWidth) { 
     if (imageWidth > imageHeight) { 
      inSampleSize = Math.round((float)imageHeight/(float)reqHeight); 
     } else { 
      inSampleSize = Math.round((float)imageWidth/(float)reqWidth); 
     } 
    } 
int inSampleSize = Math.min(imageWidth/reqWidth,imageHeight/reqHeight); 

的,你可以設置inSampleSize

options.inSampleSize = inSampleSize; 

然後最後確保你打電話,

options.inJustDecodeBounds = false; 

否則將返回位圖作爲null

  • 處理位圖關在UI線程UI線程

    處理位圖是永遠安全的所以它總是更好地完成處理後做,在後臺線程和更新UI。

  • 緩存位圖

    LruCache可從API 12,但如果你有興趣使用它下面的版本中,它也是Support Library也可用。因此,應該高效地使用圖片緩存圖片。此外,您還可以使用DiskLruCache獲取您想要的圖像,然後在外部存儲中保留更長時間。

  • 清除緩存

    有時候,當你的圖片尺寸過大,甚至緩存圖像引起OutOfMemoryError所以在這種情況下,它能夠更好地清除緩存,當您的圖像超出範圍或不用於更長的時間以便其他圖像可以被緩存。

    我創造了一個相同的演示的例子,你可以下載從here

+3

聽起來不錯,+1你先生 –

+2

管理位圖很好的答案,但'有效回收位圖'呢? – ALiGOTec

+0

爲什麼要回收位圖?如果位圖被回收再利用,則不能重複使用,這將導致不尋常的圖像下載一次又一次。所以,最好是使用Bitmap的緩存。 –

3

你的情況下,行爲與預期相同。 Honeycomb之前,recycle()無條件釋放內存。但在3.0及以上版本中,位圖是正常垃圾收集內存的一部分。你在設備上有足夠的內存,你允許JVM分配超過58M的限制,現在垃圾回收器很滿意,並且沒有回收你的位圖佔用內存的動機。

您可以通過在具有受控RAM容量的模擬器上運行來驗證此功能,或者在您的設備上加載一些耗費內存的服務 - GC將跳轉到工作狀態。您可以use DDMS來進一步調查您的內存使用情況。

您可以嘗試爲位圖內存管理的一些解決方案:Bitmaps in AndroidBitmap memory leakshttp://blog.javia.org/how-to-work-around-androids-24-mb-memory-limit/,但與official Android bitmap tips開始,在@Lalit Poptanidetailed answer解釋。

需要注意的是移動的位圖到OpenGL的內存作爲紋理有一些性能問題(但完美的,如果你將渲染通過OpenGL的到底這些位圖)。紋理和malloc解決方案都要求您明確釋放不再使用的位圖內存。

1

爲了解決您的困境,我相信這是預期的行爲。

如果你想釋放的內存,你可以偶爾叫System.gc(),但實際上,你應該在大多數情況下讓它管理垃圾收集本身。

我的建議是,你保持某種其通過計算每個位被佔用的字節數跟蹤它自己的內存使用的一個簡單的緩存(URL /文件名,位圖)。

/** 
* Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config 
* @param width 
* @param height 
* @param config 
* @return 
*/ 
public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){ 
    long pixels=width*height; 
    switch(config){ 
    case ALPHA_8: // 1 byte per pixel 
     return pixels; 
    case ARGB_4444: // 2 bytes per pixel, but depreciated 
     return pixels*2; 
    case ARGB_8888: // 4 bytes per pixel 
     return pixels*4; 
    case RGB_565: // 2 bytes per pixel 
     return pixels*2; 
    default: 
     return pixels; 
    } 
} 

然後你查詢的應用程序是使用多少內存,多少是可用的,可能採取一半,並儘量保持下,總的圖像緩存大小,通過簡單地移除(解引用)舊圖像你的名單,當你是在這個極限來臨,不能回收。讓垃圾收集器清理位圖時,它們都從緩存中取消並且沒有被任何視圖使用。

/** 
* Calculates and adjusts the cache size based on amount of memory available and average file size 
* @return 
*/ 
synchronized private int calculateCacheSize(){ 
    if(this.cachedBitmaps.size()>0){ 
     long maxMemory = this.getMaxMemory(); // Total max VM memory minus runtime memory 
     long maxAllocation = (long) (ImageCache.MEMORY_FRACTION*maxMemory); 
     long avgSize = this.bitmapCacheAllocated/this.cachedBitmaps.size(); 
     this.bitmapCacheSize = (int) (maxAllocation/avgSize); 
    } 
    return this.bitmapCacheSize; 
} 

我會建議你使用recycle()走就走,它會導致很多斷斷續續的例外(比如當看似敲定觀點嘗試訪問再生位圖),並在一般看來馬車。

+0

嗯,我已經推遲了對象,但在我回收它之前,我會嘗試一下,不要調用回收() –

1

您必須非常小心處理Android上的位圖。讓我改述一下:即使在一個有4個RAM的系統上,你也必須小心處理位圖。這些人有多大,你有很多?如果它很大,你可能不得不砍碎並鋪平它。請記住,您使用的視頻RAM是與系統RAM不同的動物。

預蜂窩,所述位圖被分配在C++層,使RAM的使用是不可見的Java和不能被垃圾收集器來訪問。具有RGB24色彩空間的3 MP未壓縮位圖使用大約9-10兆字節(大約2048x1512)。所以,更大的圖像可以很容易地填滿你的堆。另外請記住,無論用於視頻RAM(有時是專用RAM,有時與系統共享),數據通常都是未壓縮存儲的。

基本上,如果你的目標預蜂窩設備,則幾乎必須管理的位圖對象,如果你是編碼C++程序。運行位圖recycle()onDestory()通常在沒有多少圖像的情況下工作,但如果屏幕上有大量圖像,則可能需要在運行中處理它們。另外,如果啓動另一個活動,則可能需要考慮將邏輯放入onPause()和onResume()。

你也可以使用Android文件系統或SQLite緩存不在視頻RAM中的圖像。如果您使用.jpg或帶有大量重複數據的.png格式,您可能無法將其緩存到RAM中

+1

這種情況下,通過應用程序本身分頁的圖像數量很大。我隨後用循環方式處理,真的很好。 –

2

肯定@Lalit Poptani答案是做到這一點的方法,您應該如果它們非常大,真的會縮放你的Bitmaps。如果可能,最好的方法是完成server-side,因爲您還將減少NetworkOperation時間。

關於MemoryCacheDiskCache這又是做到這一點的最好辦法,但我還是建議使用現有的庫,這正是這麼做的(Ignition),您將節省大量的時間執行,還有很多內存泄漏,因爲你的HeapGC之後沒有被清空,我可以假設你也可能有一些memory leaks

+0

好吧,我的字面意思是「搜索和摧毀」內存泄漏,內存是如預期般繞過導航。 –