內(nèi)存泄漏,這個(gè)名詞很常見,那什么是內(nèi)存泄漏呢?通俗易懂的來(lái)說 就是對(duì)象已經(jīng)不需要被使用了,但是還沒有被釋放。
專業(yè)一點(diǎn)來(lái)說下安卓的泄漏,生命周期較長(zhǎng)的對(duì)象持有生命周期較短的對(duì)象的引用。內(nèi)存不在GC掌控之內(nèi)了。當(dāng)一個(gè)對(duì)象已經(jīng)不需要再使用了,本該被回收時(shí),而有另外一個(gè)正在使用的對(duì)象持有它的引用從而就導(dǎo)致,對(duì)象不能被回收。這種導(dǎo)致了本該被回收的對(duì)象不能被回收而停留在堆內(nèi)存中,就產(chǎn)生了內(nèi)存泄漏。
下面我們將從一個(gè)handler例子 來(lái)分析內(nèi)存泄漏。

這是一個(gè)在Activity 中 handler發(fā)送一個(gè)延遲2s的消息,緊接著finish()界面,來(lái)模擬界面崩潰??吹竭@里,請(qǐng)大家閉上眼睛來(lái)思考一下,這個(gè)會(huì)不會(huì)造成內(nèi)存泄漏?
好睜開眼,繼續(xù)往下看,上面當(dāng)做一個(gè)遺留問題 來(lái)思考。


先解釋一下 最后一行 這個(gè)finish(),這里也是模擬界面崩潰的一個(gè)操作。解決內(nèi)存泄漏 就是需要在程序正常運(yùn)行和非正常運(yùn)行時(shí),保證內(nèi)存的穩(wěn)定,防止內(nèi)存泄漏。
直入主題,下面帶大家來(lái)看為啥會(huì)造成泄漏。

這是在追尋源碼時(shí),里面的一個(gè)死循環(huán)+一個(gè)鎖。這兩個(gè)東西 其實(shí)會(huì)帶出很多的面試點(diǎn) 比如死循環(huán)為什么不會(huì)造成anr等。加鎖之后 ,延遲發(fā)送的消息是否還準(zhǔn)確等。這個(gè)如果大家感興趣 以后我會(huì)再寫一個(gè)專題 來(lái)詳細(xì)翻源碼來(lái)看具體原因。咱們繼續(xù):
咱們直入主題,handler.sendxx: MessageQueue.enqueueMessage()消息入隊(duì)列
Thread -> Looper.loop():MessageQueue.next()? -> handler.dispatchMessage()消息出隊(duì)列,從上面的源碼中 我們看到了 有一句msg.target==null的判斷。 這個(gè)target是啥呢?在學(xué)習(xí)的時(shí)候 大家可以思考下,下面給大家看一下源碼。

沒想到盡然是一個(gè)handler。那么現(xiàn)在內(nèi)存泄漏就很明顯 了。handler 是message 的成員變量。當(dāng)我們使用handler的時(shí)候 handler里面未處理完的消息 還持有handler的引用。相信初級(jí)安卓開發(fā)的人員 都知道怎么去解決這個(gè)問題,調(diào)用他的移除消息方法,這是處理方式,如果別人問道原因和原理會(huì)一臉懵逼,或者會(huì)答handler持有外部類的引用,其實(shí)只是答對(duì)了一部分 或者也可以說根本沒有答。最后來(lái)一筆畫龍點(diǎn)睛的總結(jié),為什么造成泄漏。Message 持有handler的引用,handler持有當(dāng)前界面的引用,如果當(dāng)前界面銷毀 ,消息沒有處理完 就造成了泄漏。
順便帶大家來(lái)了解一下總結(jié)LeakCanary的原理
leacCanary添加依賴不需要初始化,根據(jù)源碼可知道會(huì)先走到contprovider 的onCreate()方法,完成自動(dòng)初始化。
1、LeakCanary只能檢測(cè)Activity已經(jīng)與Activity相關(guān)聯(lián)的對(duì)象,通過注冊(cè)生命周期。
2、它的實(shí)現(xiàn)是通過:
監(jiān)聽Activity的生命周期,在onDestroy時(shí),去分析這個(gè)要銷毀的Activity以及與的相關(guān)的對(duì)象是否存在內(nèi)存泄漏
采用了弱引用和弱引用Queue的特性進(jìn)行判斷引用對(duì)象即Activity是否已經(jīng)被回收,具體原理是: KeyedWeakReference繼承了WeakReference,它也是弱引用的實(shí)現(xiàn)類,一般我們創(chuàng)建弱引用對(duì)象時(shí)都是只傳與之關(guān)聯(lián)的對(duì)象,這里還傳遞使用了一個(gè)WeakReferenceQueue,這個(gè)用來(lái)干嘛的?這個(gè)queue它的作用是用來(lái)存放已經(jīng)被回收的弱引用對(duì)象,如果queue中有這個(gè)弱引用就代表這個(gè)弱引用所關(guān)聯(lián)的對(duì)象已經(jīng)被回收;而如果queue中沒有這個(gè)弱引用對(duì)象則代表這個(gè)弱引用所關(guān)聯(lián)的對(duì)象還沒被回收,是存在內(nèi)存泄漏的可能性。
如果GC之后引用對(duì)象還存活,那么通過Debug的dumpHprofData方法調(diào)用nativie方法讓虛擬機(jī)去抓取堆內(nèi)存快照(File heapDumpFile = heapDumper.dumpHeap();),根據(jù)堆內(nèi)存快照文件的格式hprof協(xié)議、GC ROOT、GC引用鏈的原理進(jìn)行解析、分析堆內(nèi)存快照,heapdumpListener.analyze(。。。) 開啟HeapAnalyzerService.class 服務(wù),得到分析結(jié)果信息類,sendResultToListener,最后將這個(gè)分析結(jié)果發(fā)送給監(jiān)聽者,讓結(jié)果監(jiān)聽者處理這個(gè)分析結(jié)果。將分析結(jié)果通過通知欄的方式顯示出來(lái),如果存在內(nèi)存泄漏則當(dāng)點(diǎn)擊通知欄時(shí)會(huì)打開DispalyLeakActivity將內(nèi)存泄漏的信息展示處理
面試核心流程。以上所有內(nèi)容如有錯(cuò)誤 歡迎大家指出。共同交流。