簡體   English   中英

Android GC內存碎片失敗。 解決方法?

[英]Android GC memory fragmentation fail. Workaround?

我正在測試android 3.1,大堆大小的選項,大約250M的內存可用。

每當我點擊應用程序首選項中的“測試”按鈕時,我都會設置以下代碼:

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

我有足夠的記憶 - 我可以毫無問題地按幾下按鈕。

但是,如果我一直按下按鈕,最終(少於20次點擊)它會以OutOfMemory消失。 [通常在android.graphics.Bitmap.nativeCreate(Native Method)中]

沒有其他事情發生 - 我永遠不必離開PreferencesActivity。 當我點擊按鈕時,還會顯示一個小Toast,因此正在進行少量其他UI活動。

這是由於碎片,還是Android Bitmap代碼和/或GC中的一個可怕的錯誤? 或者我只是在做一些愚蠢的事情? (請讓它成為愚蠢的......)

有人有解決方法嗎? 因為上面的代碼完全代表了我的代碼每次用戶調用它時所要做的事情,而且現在盡管細致地清除了變量,但它在幾次使用后就會消失。 (這已經讓我瘋了很久了!)

[更新]

我已經確認這是一個碎片問題或gc錯誤,因為堆轉儲顯示我在閑置(沒有泄漏)時只使用5.6M處理期間大約26M達到峰值。 (此外,本機堆保持在4M以下。)雖然java堆同時在我的測試設備上一直增長到280M限制,此時我開始獲得OutOfMemory異常。 所以我在峰值時只使用了10%的可用堆,但獲得了OutOfMemory。

[不幸的是,添加對System.gc()的調用修復了我上面給出的簡單測試用例。 我說不幸,因為(A)它不應該有所作為,(B)因為我已經在我的實際代碼中定期調用它,所以這意味着我上面的簡單測試用例太簡單了。

有沒有其他人遇到這個? 任何解決方法? 有沒有一種優雅的方式來重新啟動我的應用程序?

[更新]

以下版本可以在3到4次調用中可靠地導致OutOfMemory(按下按鈕):

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();

內存跟蹤顯示堆在每次調用時穩定增長,直到達到限制並死亡。 如果我刪除三個分配中的任何一個,它就會達到平衡並無限期地存活下來。 刪除除最后一個gc()之外的所有gc()會導致它稍早死亡。

我會說這是一個碎片問題,而不是gc bug本身。 如果有人知道如何解決它,請告訴我。 int []分配用於編寫位圖,因此我沒有選擇將其分配為2d數組(android Bitmap庫的限制)。

這是另一個SO頁面,顯然有一個解決此問題的方法:

將圖像加載到Bitmap對象時出現奇怪的內存不足問題

具體來說,以法蓮的回答(摘錄):

“1)每次執行BitmapFactory.decodeXYZ()時,請確保將inPurgeable設置為true的BitmapFactory.Options傳入(並且最好將inInputShareable也設置為true)。

“2)永遠不要使用Bitmap.createBitmap(width,height,Config.ARGB_8888)。我的意思是從來沒有!我幾乎沒有通過幾次沒有引起內存錯誤。沒有回收量(),System.gc(),它總是引發異常。另一種實際工作方式是在你的drawables中使用虛擬圖像(或者你使用上面的步驟1解碼的另一個Bitmap),將其重新縮放到你想要的任何位置,然后操縱生成的Bitmap(比如將它傳遞給Canvas以獲得更多樂趣。所以,你應該使用的是:Bitmap.createScaledBitmap(srcBitmap,width,height,false)。如果由於某種原因你必須使用強力創建方法,那么在最少通過Config.ARGB_4444。“

在評論中,有人說這解決了他們的問題,這與OP的問題非常相似。

我想補充一點,Diane Hackborn評論說,從3.0開始,Android不再從本機堆中分配位圖,而是直接從常規堆中分配它們。 這可能會使您的本機堆數字無關緊要。 請參閱hackbod對此頁面的評論:

Android中的位圖

我想這意味着Honeycomb關於位圖分配的一個相當大的變化,因此這可以解釋為什么存在這種分配的錯誤(如果存在)。 我不知道這個改變對recycle()命令有什么影響,但根據Ephraim的上述評論,答案可能是“不是很好的”。

最后,

使用largeHeap來攝取巨大的位圖可能會被視為與其他應用程序不相稱,特別是如果你接近設備的物理極限。 我不確定你怎么能避免這種情況,但是當你的應用程序在其他應用程序上執行時,請為許多onPause()/ onResume()活動做好准備,然后他們會退回到你的應用程序上。 這個答案包括對此的討論:

在Android中檢測應用程序堆大小

為了防止碎片,你可以只分配大數組和Bitmap一次並重用它。

對於Android,有一些警告,因為Android試圖在某種程度上管理你的應用程序的資源。 例如,如果不可見,可以卸載ActivityView ,如果它們再次可見,則可以在以后重新運行。 因此,大型事物最好由Application對象或static位置存儲。

如果這只是用於某些首選項對話框,您應該在第一次使用時保留它,但之后保留它,以便在每次運行時不使用那么多內存。 如果很少使用它,您可能應該在保留首選項屏幕后重新啟動應用程序。 如果做得好,用戶不需要注意它,並且您將再次獲得新的和記憶友好的過程。

有一篇關於在an​​droid developer.android.com上管理位圖內存的非常詳細的文章:

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

基本上他們建議使用Bitmap.recycle()以避免Android 2.3.3及更低版本的OutOfMemoryError錯誤。 根據文檔,它釋放與Bitmap對象關聯的本機對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM