电竞比分网-中国电竞赛事及体育赛事平台

分享

內(nèi)存泄漏研究 | Jason's Blog

 icecity1306 2015-01-08

概念

根搜索算法

Android虛擬機的垃圾回收采用的是根搜索算法。GC會從根節(jié)點(GC Roots)開始對heap進行遍歷。到最后,部分沒有直接或者間接引用到GC Roots的就是需要回收的垃圾,會被GC回收掉。

根搜索算法相比引用計數(shù)法很好的解決了循環(huán)引用的問題。舉個例子,Activity有View的引用,View也有Activity的引用,之前我還嘗試去源代碼里找Activity何時和View斷開連接是大錯特錯了。當Activity finish掉之后,Activity和View的循環(huán)引用已成孤島,不再引用到GC Roots,無需斷開也會被回收掉。

內(nèi)存泄漏

Android內(nèi)存泄漏指的是進程中某些對象(垃圾對象)已經(jīng)沒有使用價值了,但是它們卻可以直接或間接地引用到gc roots導(dǎo)致無法被GC回收。無用的對象占據(jù)著內(nèi)存空間,使得實際可使用內(nèi)存變小,形象地說法就是內(nèi)存泄漏了。

場景

  • 類的靜態(tài)變量持有大數(shù)據(jù)對象
    靜態(tài)變量長期維持到大數(shù)據(jù)對象的引用,阻止垃圾回收。
  • 非靜態(tài)內(nèi)部類的靜態(tài)實例
    非靜態(tài)內(nèi)部類會維持一個到外部類實例的引用,如果非靜態(tài)內(nèi)部類的實例是靜態(tài)的,就會間接長期維持著外部類的引用,阻止被回收掉。
  • 資源對象未關(guān)閉
    資源性對象如Cursor、File、Socket,應(yīng)該在使用后及時關(guān)閉。未在finally中關(guān)閉,會導(dǎo)致異常情況下資源對象未被釋放的隱患。
  • 注冊對象未反注冊
    未反注冊會導(dǎo)致觀察者列表里維持著對象的引用,阻止垃圾回收。
  • Handler臨時性內(nèi)存泄露
    Handler通過發(fā)送Message與主線程交互,Message發(fā)出之后是存儲在MessageQueue中的,有些Message也不是馬上就被處理的。在Message中存在一個 target,是Handler的一個引用,如果Message在Queue中存在的時間越長,就會導(dǎo)致Handler無法被回收。如果Handler是非靜態(tài)的,則會導(dǎo)致Activity或者Service不會被回收。
    由于AsyncTask內(nèi)部也是Handler機制,同樣存在內(nèi)存泄漏的風險。
    此種內(nèi)存泄露,一般是臨時性的。

預(yù)防

  • 不要維持到Activity的長久引用,對activity的引用應(yīng)該和activity本身有相同的生命周期。
  • 盡量使用context-application代替context-activity
  • Activity中盡量不要使用非靜態(tài)內(nèi)部類,可以使用靜態(tài)內(nèi)部類和WeakReference代替。

檢測

靜態(tài)檢測

靜態(tài)檢測主要是檢測資源未關(guān)閉的情況,Eclipse和Android Studio都可以檢測出IO或者Socket未關(guān)閉的情況,然后在finally中關(guān)閉即可。

動態(tài)監(jiān)測

動態(tài)檢測主要是依靠MAT這個工具。2011年Google IO有一個主題演講,非常詳細地講解了內(nèi)存泄露的檢測,包含MAT工具的使用,值得一看。
我在某項目中使用MAT檢測,發(fā)現(xiàn)一處內(nèi)存泄漏,分享一下過程。
從首頁到商戶列表到商戶詳情再退回首頁執(zhí)行Dump HPROF File,查看MAT中的Histogram,過濾Activity后結(jié)果如下: 

 

Histogram
仍然存在ShopInfoActivity的實例,選中右鍵點擊Merge Shortest Paths to GC Roots,結(jié)果如下: 

 

此處輸入圖片的描述
可以看到ShopDatabase中維持著ShopInfoActivity的引用,查看源代碼如下:


public class ShopDatabase {

private static ShopDatabase instance;
public static ShopDatabase getInstance(Context context) {
if (instance == null && context != null) {
instance = new ShopDatabase(context);
}
return instance;
}
protected Context context;

}
很明顯,靜態(tài)變量instance長期持有context的引用,造成內(nèi)存泄露。
所以動態(tài)檢測內(nèi)存泄露的一個簡單思路就是隨意操作APP,最后返回首頁,然后用MAT檢測,查看是否存在Activity多于一個或者Activity不正常存在的問題。

參考資料

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多