Android應用防內存泄露小結

這兩天在寫完代碼之餘,算是有時間來review下整個工程的代碼,以內存泄露爲出發點,對代碼進行了詳細的解讀。
結合項目主要總結了兩點:

1. 對Context引用的防範;
    在Android應用中, 對Context對象的引用隨處可見, 很多的事情需要有Context對象的方法才能完成。然後, 很多時候,我們對Context對象的引用並沒有考慮那麼全面(以爲一切事情都有JVM GC爲我們做),但實際上,任何應用開發,只有在深刻理解其運行環境才能做大性能最大化(至少可以做到不那麼糟糕)。在很多情況下, 我們引用Context對象是這樣的,一,是在調用別的接口(需要Context對象作爲參數)的時候,只管把this傳進去而不管其他;二,是在需要別的Context對象的時候,讓調用方傳遞進來,而不管生命週期這些問題。這會導致一些問題, 根本原因是一樣的,即調用方和被調用方生命週期不一致,後果是在一個Activity結束掉後,Context對象仍然被引用。如果這種情況是增量式的, 內存泄露問題就出現了。
    增量式的情況如:
        a). Activity內創建非靜態Handler對象, 或者靜態Handler對象但是沒有使用對Activity的弱引用;
            --> 原因: 主線程的創建的Handler會與存儲在ThreadLocal裏的MessageQueue綁定,而主線程的ThreadLocal的生命週期就是整個進程的生命週期,所以通常長於某個Activity。如果用以上情況創建Handler,因爲創建的內部類對象隱式的含有對外部類(即Activity)的引用,所以每次new一個Activity並創建Handler對象的同時, 就意味着將自身的引用儲存在了ThreadLocal裏,這樣,當Activity結束時,(如果進程沒銷燬),那麼Activity對象的堆內存仍然不會收回,而在反覆啓動結束這個Activity之後,就出現了內存泄露。
        b). 用生命週期較長的(如靜態的)集合將mContext儲存起來
2. Bitmap的回收
    Bitmap是圖片儲存在內存裏的方式,通常佔用內存較大,而負責分配內存的是native方法,JVM在GC的時候是不會回收到native裏面的內存的,所以在Bitmap對象用完之後 ,需要我們手動調用recycle方法來通知native方法free掉分配的內存。


附:內存泄露檢測方法(這裏只針對檢測Activity的內存泄露)

1. 啓動應用,在要檢測的Activity啓動之前,Cause GC一下,顯示當前內存使用情況;

圖1,啓動應用,並在啓動待檢測的Activity之前的內存使用情況

2. 啓動要檢測的Activity,然後finish,然後啓動,然後finish,如此反覆多次.......最後finish這個Activity(即回到步驟1時的狀態), 然後Cause   GC一下,
如果發現內存使用量是逐漸上升,那麼就說明這個Activity存在內存泄露(具體問題定位看圖3)

圖2, 反覆啓動銷燬待檢測Activity, 查看內存使用情況
(即看Activity每次銷燬後內存是否被回收,如果不停增加說明每次啓動後存在泄漏)

3. 在步驟一、二Casuse GC的時候, Dump一下,用MAT插件打開, 然後把兩次的Histogram進行對比,可以清楚的看到是哪些
類型的數據在遞增,而沒有得到回收。(具體可以定位到Class信息, 只需右鍵Class Name這一欄的信息,然後選擇list objects,選擇incoming這一項,如圖4)

圖3. 對比1與2情況下的內存使用

4. 查看具體的對象佔用內存情況,圖例中可以看到問題所在——Bitmap對象內存沒有被回收。

圖4. 針對3所列出的項,右鍵查看具體的對象佔用內存情況
(以便更容易定位到問題所在)

尾註:內存的合理分配與回收是應用性能的保障,特別是移動端對用戶體驗要求高的應用,因爲每一次GC操作,系統會暫停所有的線程運行。如果內存的使用不合理,導致GC過於頻繁會使得界面的繪製出現失貞的情況,從而出現卡頓導致用戶體驗不好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章