Android 內(nèi)存泄漏分析心得

前言

對于C++來說疯溺,內(nèi)存泄漏就是new出來的對象沒有delete,俗稱野指針玉雾;對于Java來說劈榨,就是new出來的Object 放在Heap上無法被GC回收;本文通過QQ和Qzone中內(nèi)存泄漏實例來講android中內(nèi)存泄漏分析解法和編寫代碼應注意的事項。

Java 中的內(nèi)存分配

靜態(tài)儲存區(qū):編譯時就分配好,在程序整個運行期間都存在。它主要存放靜態(tài)數(shù)據(jù)和常量想虎;

棧區(qū):當方法執(zhí)行時,會在棧區(qū)內(nèi)存中創(chuàng)建方法體內(nèi)部的局部變量叛拷,方法結束后自動釋放內(nèi)存舌厨;

堆區(qū):通常存放 new 出來的對象。由 Java 垃圾回收器回收忿薇。

四種引用類型的介紹

強引用(StrongReference):JVM 寧可拋出 OOM 裙椭,也不會讓 GC 回收具有強引用的對象;

軟引用(SoftReference):只有在內(nèi)存空間不足時署浩,才會被回的對象骇陈;

弱引用(WeakReference):在 GC 時,一旦發(fā)現(xiàn)了只具有弱引用的對象瑰抵,不管當前內(nèi)存空間足夠與否你雌,都會回收它的內(nèi)存;

虛引用(PhantomReference):任何時候都可以被GC回收,當垃圾回收器準備回收一個對象時婿崭,如果發(fā)現(xiàn)它還有虛引用拨拓,就會在回收對象的內(nèi)存之前,把這個虛引用加入到與之關聯(lián)的引用隊列中氓栈。程序可以通過判斷引用隊列中是否存在該對象的虛引用渣磷,來了解這個對象是否將要被回收∈谑荩可以用來作為GC回收Object的標志醋界。

我們常說的內(nèi)存泄漏是指new出來的Object無法被GC回收,即為強引用:

內(nèi)存泄漏發(fā)生時的主要表現(xiàn)為內(nèi)存抖動提完,可用內(nèi)存慢慢變少:

Andriod中分析內(nèi)存泄漏的工具MAT

MAT(Memory Analyzer Tools)是一個 Eclipse 插件形纺,它是一個快速、功能豐富的JAVA heap分析工具徒欣,它可以幫助我們查找內(nèi)存泄漏和減少內(nèi)存消耗逐样。

MAT 插件的下載地址:www.eclipse.org/mat

MAT 使用方法介紹:

http://www.cnblogs.com/larack/p/6071209.html

QQ和Qzone內(nèi)存泄漏如何監(jiān)控

QQ和Qzone 的內(nèi)存泄漏采用SNGAPM解決方案,SNGAPM是一個性能監(jiān)控打肝、分析的統(tǒng)一解決方案脂新,它從終端收集性能信息,上報到一個后臺粗梭,后臺將監(jiān)控類信息聚合展示為圖表争便,將分析類信息進行分析并提單,通知開發(fā)者断医;

SNGAPM由App(MagnifierApp)和

web server(MagnifierServer)兩部分組成始花;

MagnifierApp在自動內(nèi)存泄漏檢測中是一個銜接檢測組件(LeakInspector)和自動化云分析(MagnifierCloud)的中間性平臺,它從LeakInspector的內(nèi)存dump自動化上傳MagnifierServer孩锡;

MagnifierServer后臺會定時提交分析任務到MagnifierCloud;

MagnifierCloud分析結束之后會更新數(shù)據(jù)到magnifier web上亥贸,同時以bug單形式通知開發(fā)者躬窜。

常見的內(nèi)存泄漏案例

case 1. 單例造成的內(nèi)存泄露

單例的靜態(tài)特性導致其生命周期同應用一樣長。

解決方案:

將該屬性的引用方式改為弱引用;

如果傳入Context炕置,使用ApplicationContext;

example:

泄漏代碼片段

case 2. InnerClass匿名內(nèi)部類

在Java中荣挨,非靜態(tài)內(nèi)部類 和 匿名類 都會潛在的引用它們所屬的外部類,但是朴摊,靜態(tài)內(nèi)部類卻不會默垄。如果這個非靜態(tài)內(nèi)部類實例做了一些耗時的操作,就會造成外圍對象不會被回收甚纲,從而導致內(nèi)存泄漏口锭。

解決方案:

將內(nèi)部類變成靜態(tài)內(nèi)部類;

如果有強引用Activity中的屬性,則將該屬性的引用方式改為弱引用;

在業(yè)務允許的情況下,當Activity執(zhí)行onDestory時鹃操,結束這些耗時任務;

example:

case 3. Activity Context 的不正確使用

在Android應用程序中通尘麓纾可以使用兩種Context對象:Activity和Application。當類或方法需要Context對象的時候常見的做法是使用第一個作為Context參數(shù)荆隘。這樣就意味著View對象對整個Activity保持引用恩伺,因此也就保持對Activty的所有的引用。

假設一個場景椰拒,當應用程序有個比較大的Bitmap類型的圖片晶渠,每次旋轉是都重新加載圖片所用的時間較多。為了提高屏幕旋轉是Activity的創(chuàng)建速度燃观,最簡單的方法時將這個Bitmap對象使用Static修飾褒脯。 當一個Drawable綁定在View上,實際上這個View對象就會成為這份Drawable的一個Callback成員變量仪壮。而靜態(tài)變量的生命周期要長于Activity憨颠。導致了當旋轉屏幕時,Activity無法被回收积锅,而造成內(nèi)存泄露爽彤。

解決方案:

使用ApplicationContext代替ActivityContext,因為ApplicationContext會隨著應用程序的存在而存在缚陷,而不依賴于activity的生命周期适篙;

對Context的引用不要超過它本身的生命周期,慎重的對Context使用“static”關鍵字箫爷。Context里如果有線程嚷节,一定要在onDestroy()里及時停掉。

example:

case 4. Handler引起的內(nèi)存泄漏

當Handler中有延遲的的任務或是等待執(zhí)行的任務隊列過長虎锚,由于消息持有對Handler的引用硫痰,而Handler又持有對其外部類的潛在引用,這條引用關系會一直保持到消息得到處理窜护,而導致了Activity無法被垃圾回收器回收效斑,而導致了內(nèi)存泄露。

解決方案:

可以把Handler類放在單獨的類文件中柱徙,或者使用靜態(tài)內(nèi)部類便可以避免泄露;

如果想在Handler內(nèi)部去調(diào)用所在的Activity,那么可以在handler內(nèi)部使用弱引用的方式去指向所在Activity.使用Static + WeakReference的方式來達到斷開Handler與Activity之間存在引用關系的目的缓屠。

Solution

case 5. 注冊監(jiān)聽器的泄漏

系統(tǒng)服務可以通過Context.getSystemService 獲取,它們負責執(zhí)行某些后臺任務护侮,或者為硬件訪問提供接口敌完。如果Context 對象想要在服務內(nèi)部的事件發(fā)生時被通知,那就需要把自己注冊到服務的監(jiān)聽器中羊初。然而滨溉,這會讓服務持有Activity 的引用,如果在Activity onDestory時沒有釋放掉引用就會內(nèi)存泄漏。

解決方案:

使用ApplicationContext代替ActivityContext;

在Activity執(zhí)行onDestory時业踏,調(diào)用反注冊;

mSensorManager = (SensorManager)this.getSystemService(Context.SENSOR_SERVICE);

Solution:

mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);

下面是容易造成內(nèi)存泄漏的系統(tǒng)服務:

InputMethodManager imm = (InputMethodManager) context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);

case 6. Cursor禽炬,Stream沒有close,View沒有recyle

資源性對象比如(Cursor勤家,F(xiàn)ile文件等)往往都用了一些緩沖腹尖,我們在不使用的時候,應該及時關閉它們伐脖,以便它們的緩沖及時回收內(nèi)存热幔。它們的緩沖不僅存在于 java虛擬機內(nèi),還存在于java虛擬機外讼庇。如果我們僅僅是把它的引用設置為null,而不關閉它們绎巨,往往會造成內(nèi)存泄漏。因為有些資源性對象蠕啄,比如SQLiteCursor(在析構函數(shù)finalize(),如果我們沒有關閉它场勤,它自己會調(diào)close()關閉),如果我們沒有關閉它歼跟,系統(tǒng)在回收它時也會關閉它和媳,但是這樣的效率太低了。因此對于資源性對象在不使用的時候哈街,應該調(diào)用它的close()函數(shù)留瞳,將其關閉掉,然后才置為null. 在我們的程序退出時一定要確保我們的資源性對象已經(jīng)關閉骚秦。

Solution:

調(diào)用onRecycled()

case 7. 集合中對象沒清理造成的內(nèi)存泄漏

我們通常把一些對象的引用加入到了集合容器(比如ArrayList)中她倘,當我們不需要該對象時,并沒有把它的引用從集合中清理掉作箍,這樣這個集合就會越來越大硬梁。如果這個集合是static的話,那情況就更嚴重了胞得。

所以要在退出程序之前荧止,將集合里的東西clear,然后置為null懒震,再退出程序。

解決方案:

在Activity退出之前嗤详,將集合里的東西clear个扰,然后置為null,再退出程序葱色。

Solution

case 8. WebView造成的泄露

當我們不要使用WebView對象時递宅,應該調(diào)用它的destory()函數(shù)來銷毀它,并釋放其占用的內(nèi)存,否則其占用的內(nèi)存長期也不能被回收办龄,從而造成內(nèi)存泄露烘绽。

解決方案:

為webView開啟另外一個進程,通過AIDL與主線程進行通信俐填,WebView所在的進程可以根據(jù)業(yè)務的需要選擇合適的時機進行銷毀安接,從而達到內(nèi)存的完整釋放。

case 9. 構造Adapter時英融,沒有使用緩存的ConvertView

初始時ListView會從Adapter中根據(jù)當前的屏幕布局實例化一定數(shù)量的View對象盏檐,同時ListView會將這些View對象 緩存起來。

當向上滾動ListView時驶悟,原先位于最上面的List Item的View對象會被回收胡野,然后被用來構造新出現(xiàn)的最下面的List Item。

這個構造過程就是由getView()方法完成的痕鳍,getView()的第二個形參View ConvertView就是被緩存起來的List Item的View對象(初始化時緩存中沒有View對象則ConvertView是null)硫豆。

本文是我轉自轉自: QQ空間終端開發(fā)團隊,歡迎關注A簟(文章僅僅為了讓更多的android愛好者學習使用)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末熊响,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子抄邀,更是在濱河造成了極大的恐慌耘眨,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件境肾,死亡現(xiàn)場離奇詭異剔难,居然都是意外死亡,警方通過查閱死者的電腦和手機奥喻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門偶宫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人环鲤,你說我怎么就攤上這事纯趋。” “怎么了冷离?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵吵冒,是天一觀的道長。 經(jīng)常有香客問我西剥,道長痹栖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任瞭空,我火速辦了婚禮揪阿,結果婚禮上疗我,老公的妹妹穿的比我還像新娘。我一直安慰自己南捂,他們只是感情好吴裤,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著溺健,像睡著了一般麦牺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矿瘦,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天枕面,我揣著相機與錄音,去河邊找鬼缚去。 笑死潮秘,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的易结。 我是一名探鬼主播枕荞,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼搞动!你這毒婦竟也來了躏精?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤鹦肿,失蹤者是張志新(化名)和其女友劉穎矗烛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箩溃,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡瞭吃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了涣旨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歪架。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖霹陡,靈堂內(nèi)的尸體忽然破棺而出和蚪,到底是詐尸還是另有隱情,我是刑警寧澤烹棉,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布攒霹,位于F島的核電站,受9級特大地震影響浆洗,放射性物質發(fā)生泄漏催束。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一辅髓、第九天 我趴在偏房一處隱蔽的房頂上張望泣崩。 院中可真熱鬧,春花似錦洛口、人聲如沸矫付。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽买优。三九已至,卻和暖如春挺举,著一層夾襖步出監(jiān)牢的瞬間杀赢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工湘纵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脂崔,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓梧喷,卻偏偏與公主長得像砌左,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子铺敌,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

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