leakcanary簡單分析

今天面試有被問題leakcanary是怎么實現(xiàn)的赚窃,自己沒看過源碼只是簡單說了下庐舟,猜測是通過監(jiān)控activity的destroy生命周期方法,因為老的版本還需要自己在BaseActivity中的onDestroy方法中用RefWatcher.watch來監(jiān)控activity是否被回收。 ?回來補一下情连,隨便記錄下避免容易忘記貌矿。

注:這里不分析是如何生成的.hprof內(nèi)存輸出文件也不講解leakcanary是如何根據(jù).hprof分析出內(nèi)存泄露的引用路徑的炭菌。

1.初始化

一般在Application的onCreate中通過LeakCanary.install來初始化。并且僅在debug模式和主進程中初始化使用(leakcanary有一個專門用來分析內(nèi)存泄露用的進程);

還需要注意的是在gradle中引用時需要根據(jù)是debug還是release引用不同的包逛漫。

上面兩個圖是release下引用的包中的初始化代碼黑低,可以看出什么都沒做。我們主要看debug下的實現(xiàn)。

可以看到RefWatcher是AndroidRefWatcherBuilder通過buildAndInstall構(gòu)造來的克握。

buildAndInstall其實就是通過build構(gòu)造出一個RefWatcher蕾管,其中有幾個重要的構(gòu)造參數(shù)下面講。然后如果構(gòu)造成功則把顯示泄露的DisplayLeakActivity設置為enable的菩暗,這樣才能在手機桌面上出現(xiàn)一個Leaks的圖標掰曾,點擊圖標進入的activity就是展示所有內(nèi)存泄露分出結(jié)果的界面。 ?最后通過installOnIcsPlus來監(jiān)控activity的onDestroy生命周期方法停团。

如注釋描述旷坦,這種方式僅適合api >=14(ICE_CREAM_SANDWICH),因為通過Application來注冊監(jiān)控activity生命周期的方法在14后才加的。14前的只能用老的方式在BaseActivity的onDestroy.

到這算是真正的初始化完了佑稠。這里在講下RefWatcher構(gòu)造時重要的幾個參數(shù)秒梅。其中部分是在RefWatcher中生成部分是在AndroidRefWatcherBuilder中生成的。

a.watchExecutor:只是一個線程調(diào)度器舌胶,watch操作是在單獨的線程里執(zhí)行的捆蜀。

b.gcTrigger:觸發(fā)gc操作用的。 看注釋說Runtime的gc()比System.gc()更可能觸發(fā)gc操作哦~辆琅。

c.heapDumper: 當發(fā)現(xiàn)泄露時具體生成.hprof文件的操作漱办。

d.heapDumperListener:生成.hprof成功后回調(diào)分析的操作。

e.excludedRefs:內(nèi)置的一些分析時需要過濾掉的引用列表婉烟,這個列表中的引用出現(xiàn)泄露時可能是系統(tǒng)的原因娩井,所以過濾掉。

f.剩下的retainedKeys和queue是用來查找泄露對象用的似袁,retainedKeys中存的是沒有被gc回收掉的activity的key,而queue是用來存放activity被正扯蠢保回收的weakreference(這個weakreference包裝了activity對象的引用)。?

2.監(jiān)控Activity泄露

從之前講的可知昙衅,監(jiān)測到activity的onDestroy時會通過watch來監(jiān)控它的回收情況扬霜。這里會給activity生成一個key和一個weakreference,只不過這個weakref中還包含了對應的key. 然后把key添加到retainedKes中而涉。 最后通過ensureGoneAsync方法調(diào)用之前構(gòu)造RefWatcher時的watchExecutor異步執(zhí)行了ensureGone方法著瓶。

上面的代碼就是實現(xiàn)找出泄露的activity的方法。

首先調(diào)用removeWeaklyReachableReferences方法把當前已經(jīng)被回收的activity所對應的key從retainedKeys中刪除(因為當weakref中包裝的對象如果被gc回收時啼县,會把這個weakref放入queue中)材原。然后通過gone方法判斷key是否還在retainedKeys中,如果不在則表示activity已經(jīng)被回收季眷,watch結(jié)束余蟹。如果在則還沒回收,這時會通過gcTrigger來主動調(diào)一次gc操作子刮,然后在調(diào)用removeWeaklyReachableReferences去掉已經(jīng)初始回收的activity對應的key. 如果再次判斷gone返回還是false那么就認定這個activity泄露了威酒。 然后就是通過之前設置的heapDumper來生成.hprof。

3.hprof生成

代碼不多,因為生成hprof主要還是用的系統(tǒng)的方法葵孤。這里的大致流程就是先得到一個文件來存放生成的內(nèi)存信息担钮,然后通過mainHandler在主線程post一個顯示自定義toast的操作。并添加一個IdleHandler佛呻,把toast設置給之前定義的FutrueResult裳朋,這里僅是為了保證toast先顯示出來在執(zhí)行Debug.dumpHprofData來生成內(nèi)存信息,因為這個過程會導致gc吓著,阻塞主線程執(zhí)行導致卡頓鲤嫡。如果是直接post一個顯示toast的操作然后緊接著就執(zhí)行生成內(nèi)存信息的方法,則可能導致toast在開始時因為卡頓顯示不出來绑莺。這也是為什么它的toast里面會提示app will freeze(凍住)暖眼。隨便提下這里是通過CountDownLatch來達到子線程等待主線程確定已經(jīng)顯示toast的。用法感興趣的可以自行查找纺裁,在某些場景它也許可以幫我們更好地解決線程間需要協(xié)同的問題诫肠。

4.泄露分析

如圖,在watch方法中欺缘,如果成功生成.hprof文件則會調(diào)用heapdumpListener的analyze方法進行分析栋豫。

需要注意的是這里傳入了一個分析結(jié)果處理的class,它本質(zhì)是一個IntentService.分析結(jié)果出來后會啟動這個service來處理。

第一步:開啟一個IntentService來執(zhí)行分析操作谚殊。

第二步:通過HeapAnalyzer檢查泄露丧鸯,分析出結(jié)果。這里不深入嫩絮,感興趣可自行研究丛肢。

第三步:代碼不貼了,就是把分析結(jié)果放到intent中剿干,然后開啟之前傳入的處理分析結(jié)果的IntentService來處理蜂怎。

5.結(jié)果處理

代碼不貼了,就在DisplayLeakService的onHeapAnalyzed方法中置尔。 大概就是根據(jù)分析結(jié)果顯示一個Notification通知杠步,通知里面帶了一個顯示泄露具體信息Activity的pendingintent.


6.總結(jié)

除了可以了解這個工具實現(xiàn)監(jiān)測內(nèi)存泄露的原理和方法,還可以學到幾個新的知識點(對我來說~).

1.WeakReference和SoftReference這種引用可以配合一個ReferenceQueue使用來達到監(jiān)控對象被回收的功能榜轿。

2.Runtime.gc比System.gc更可能觸發(fā)gc操作幽歼。(System.gc其實也是調(diào)用的Runtime.gc,只是不一定每次調(diào)用都會調(diào)用到Runtime.gc)

3.CountdownLatch的作用和用法 差导。Java并發(fā)編程:CountDownLatch、CyclicBarrier和Semaphore

4.IntentService的使用猪勇。雖然了解原理但是自己沒怎么用到過设褐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子助析,更是在濱河造成了極大的恐慌犀被,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件外冀,死亡現(xiàn)場離奇詭異寡键,居然都是意外死亡,警方通過查閱死者的電腦和手機雪隧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門西轩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脑沿,你說我怎么就攤上這事藕畔。” “怎么了庄拇?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵注服,是天一觀的道長。 經(jīng)常有香客問我措近,道長溶弟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任瞭郑,我火速辦了婚禮辜御,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凰浮。我一直安慰自己我抠,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布袜茧。 她就那樣靜靜地躺著菜拓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笛厦。 梳的紋絲不亂的頭發(fā)上纳鼎,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音裳凸,去河邊找鬼贱鄙。 笑死,一個胖子當著我的面吹牛姨谷,可吹牛的內(nèi)容都是我干的逗宁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼梦湘,長吁一口氣:“原來是場噩夢啊……” “哼瞎颗!你這毒婦竟也來了件甥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤哼拔,失蹤者是張志新(化名)和其女友劉穎引有,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倦逐,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡譬正,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了檬姥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曾我。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖穿铆,靈堂內(nèi)的尸體忽然破棺而出您单,到底是詐尸還是另有隱情,我是刑警寧澤荞雏,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布虐秦,位于F島的核電站,受9級特大地震影響凤优,放射性物質(zhì)發(fā)生泄漏悦陋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一筑辨、第九天 我趴在偏房一處隱蔽的房頂上張望俺驶。 院中可真熱鬧,春花似錦棍辕、人聲如沸暮现。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栖袋。三九已至,卻和暖如春抚太,著一層夾襖步出監(jiān)牢的瞬間塘幅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工尿贫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留电媳,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓庆亡,卻偏偏與公主長得像匾乓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子又谋,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容