Android記憶體流失監測(MAT)及解決辦法

來源:互聯網
上載者:User

標籤:

http://ttlnews.blogspot.com/2010/01/attacking-memory-problems-on-android.html 

這篇文章是2010年1月份寫的,其中有些已經不適合現在的Android機制了 

我將記憶體問題分為兩種:OOM和堆疊溢位 
一個Android進程可以分配的最大堆記憶體(heap memory)為 16M(現在各個定製版本的Android系統都不一樣)

如果你將重複開啟關閉一個Activity20次,就有可能出現記憶體溢出,那麼在哪裡記憶體溢出了,在哪裡可以被GC回收? 

adb shell procrank 
使用這個命令可以獲得一些應用的記憶體資料,但是只能擷取很簡單的資料 

adb shell dumpsys meminfo 
使用這個命令可以得到更多相關資料 
如果想要瞭解更多關於記憶體配置和回收的細節就需要 Eclipse Memory Analyzer Tool (MAT)工具。 
MAT工具使用方法:http://kohlerm.blogspot.com/2009/04/analyzing-memory-usage-off-your-android.html 
http://blog.csdn.net/sgwhp/article/details/9842509 MAT可以顯示的內容  
最佳實務:

1.在onDestroy()中最好將匿名listeners都設定為null,比如view.setOnClickListener(null) ; 

2.即使沒有靜態drawable變數,但drawable對象中引用了view,view對象引用了Context,所以Activity可能會泄漏;
//Android源碼片段   Drawable對象中引用View對象
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { public void setBackgroundDrawable(Drawable background) { ..... background.setCallback(this); ...... } } public abstract class Drawable { public final void setCallback(Callback cb) { mCallback = new WeakReference<Callback>(cb); } } 

所以最好在 onDestroy() 中調用view.getBackground().setCallback(null); 
在stackoverflow的相關問答中出現了通用的unbindDrawables方法 
http://stackoverflow.com/questions/9461364/exception-in-unbinddrawables 

@Override protected void onDestroy() { super.onDestroy(); unbindDrawables(findViewById(R.id.top_layout)); System.gc(); Drawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); view.setBackground(null); //添加} if (view instanceof ViewGroup && !(view instanceof AdapterView)) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } ((ViewGroup) view).removeAllViews(); } } 
【補充】上述方法對ListView和GridView無用 3. 在onDestroy()中設定object = null 雖然設定object = null不是必要的,但是可以讓GC加快一點; 

4.讓所有對象設定為null,雖然是很方便,但是也容易出錯,最優的方法是通過MAT工具來監測哪些對象可能會泄漏,然後在 onDestroy()中置null。不然的話如果一個後台線程還在運行並且需要調用這些對象,那麼就容易出現NullPointerExceptions。 

5.Images(Bitmaps)不像一般Java對象一樣分配記憶體而是通過調用natvie方法,所以bitmap佔用了非Dalvik堆的記憶體。所以使用MAT工具時會發現用procrank和dumpsys命令查看的記憶體會比MAT中的大很多。 
【補充】 
 摘自https://developer.android.com/training/displaying-bitmaps/manage-memory.html (翻譯:) http://su1216.iteye.com/blog/1931629  
(1) Android2.2及之前版本,GC時應用的線程都會停止,這樣會引起延遲,導致效能降低。所以在2.3中添加了並發GC機制,所以只要bitmap不再被引用,所佔記憶體就馬上會被回收。 

(2) 在2.3及之前版本,bitmap的像素資料是儲存在native memory中的,並不是和bitmap對象一起儲存在Dalvik heap裡的,會導致native memory中的像素資料不能被顯式回收,造成應用超出記憶體大小限制而崩潰。 
在2.3 (API level 11)之後的版本,像素資料就和bitmap對象一起儲存在Dalvik heap上了。 

6.載入網路如片在Android中會比較難以操作,只是載入幾個200x200像素的圖片就會導致記憶體佔用的暴漲,20k大小的200x200的png圖片需要佔用160K左右的記憶體(每個像素佔用4bytes)[200x200x4/1024 = 156.25k ]推薦使用下列方法【摘自 http://android-developers.blogspot.com/2008/09/android-photostream.html】 
(1) 首先獲得圖片的的大小(像素)
BitmapFactory.Options options = new BitmapFactory.Options(); //只載入圖片的部分資訊,減少記憶體佔用   options.inJustDecodeBounds = true; Bitmap tmpBitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(new URL(url).openStream()), null, options); //擷取圖片的長寬像素   int height = options.outHeight; int width = options.outWidth; 

(2) 這樣就在下載網狀圖片之前就可以知道圖片的大小了,然後根據最終需要顯示的圖片大小進行壓縮(通常Android會在繪製的時候縮放圖片) 

options = new BitmapFactory.Options(); options.inSampleSize = sampleSize;//壓縮比列,如果是3,就會壓縮到1/3 bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(new URL(url).openStream()), null, options); 

這樣擷取的圖片記憶體佔用會小一點。 

(3) 更好的辦法是將圖片下載到本地,在使用的時候根據大小重新縮放。將不再需要圖片對象時調用bitmap.recyce()來釋放掉圖片的記憶體佔用,但是如果你調用了recycle(),之後又試圖繪製這個bitmap,你會得到 錯誤:“Canvas: trying to use a recycled bitmap”(適合Android2.3及之前版本)。 

7.ListView的convertView緩衝方式 【網上資料很多】 

8.debug的時候會保持對象處於可用狀態,記憶體不能被回收。所以記憶體分析(使用MAT工具)的時候不要使用debug。並且在heap dump之前多進行幾次GC操作【原因:http://groups.google.com/group/android-developers/browse_thread/thread/7b0ea57d9507d33f】 


文章的回複裡有個問題 
問題: 
我寫了一個簡單的demo,只有兩個activity,重複開啟SecondActivity 6次,使用MAT工具查看記憶體發現有6個SecondActivity對象,為什麼會這樣? 
回答: 
可能導致上述問題的原因 
(1) 怎麼開啟和關閉SecondActivity的,是通過Intent嗎?是通過硬體返回按鈕關閉SecondActivity的嗎? 
(2) 在SecondActivity中重載onDestroy(),打上log,重複開啟SecondActivity 6次,onDestroy()中的log會列印6次嗎? 
(3) 用多快速度重新開啟和關閉SecondActivity的?最好是當看見一個GC log時再重新開啟,可能是還沒有來得及GC,所以記憶體中會有多個對象(可有可能是你的demo記憶體佔用太小,沒有達到需要GC的條件) 
(4)檢查一下6個SecondActivity對象的狀態是否為unknown,MAT也會統計unkown狀態的對象。【補充:可以使用adb shell dumpsys meminfo 命令查看當前記憶體中Activity存在的數目】 

Android記憶體流失監測(MAT)及解決辦法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.