內存泄露從入門到精通三部曲之排查方法篇

騰訊Bugly特約做者: 姚潮生vue

最原始的內存泄露測試

重複屢次操做關鍵的可疑的路徑,從內存監控工具中觀察內存曲線,是否存在不斷上升的趨勢且不會在程序返回時明顯回落。
這種方式能夠發現最基本,也是最明顯的內存泄露問題,對用戶價值最大,操做難度小,性價比極高。android


MAT內存分析工具

2.1 MAT分析heap的總內存佔用大小來初步判斷是否存在泄露markdown

在Devices 中,點擊要監控的程序。app

點擊Devices視圖界面中最上方一排圖標中的「Update Heap」eclipse

點擊Heap視圖函數

點擊Heap視圖中的「Cause GC」按鈕工具

到此爲止需檢測的進程就能夠被監視。測試

Heap視圖中部有一個Type叫作data object,即數據對象,也就是咱們的程序中大量存在的類類型的對象。在data object一行中有一列是「Total Size」,其值就是當前進程中全部Java數據對象的內存總量,通常狀況下,這個值的大小決定了是否會有內存泄漏。能夠這樣判斷:ui

進入某應用,不斷的操做該應用,同時注意觀察data object的Total Size值,正常狀況下Total Size值都會穩定在一個有限的範圍內,也就是說因爲程序中的的代碼良好,沒有形成對象不被垃圾回收的狀況。lua

因此說雖然咱們不斷的操做會不斷的生成不少對象,而在虛擬機不斷的進行GC的過程當中,這些對象都被回收了,內存佔用量會會落到一個穩定的水平;反之若是代碼中存在沒有釋放對象引用的狀況,則data object的Total Size值在每次GC後不會有明顯的回落。隨着操做次數的增多Total Size的值會愈來愈大,直到到達一個上限後致使進程被殺掉。

2.2 MAT分析hprof來定位內存泄露的緣由所在。

這是出現內存泄露後使用MAT進行問題定位的有效手段。

A)Dump出內存泄露當時的內存鏡像hprof,分析懷疑泄露的類:

B)分析持有此類對象引用的外部對象

C)分析這些持有引用的對象的GC路徑

D)逐個分析每一個對象的GC路徑是否正常

從這個路徑能夠看出是一個antiRadiationUtil工具類對象持有了MainActivity的引用致使MainActivity沒法釋放。此時就要進入代碼分析此時antiRadiationUtil的引用持有是否合理(若是antiRadiationUtil持有了MainActivity的context致使節目退出後MainActivity沒法銷燬,那通常都屬於內存泄露了)。

2.3 MAT對比操做先後的hprof來定位內存泄露的根因所在。

爲查找內存泄漏,一般須要兩個 Dump結果做對比,打開 Navigator History面板,將兩個表的 Histogram結果都添加到 Compare Basket中去

A) 第一個HPROF 文件(usingFile > Open Heap Dump ).

B)打開Histogram view.

C)在NavigationHistory view裏 (若是看不到就從Window >show view>MAT- Navigation History ), 右擊histogram而後選擇Add to Compare Basket .

D)打開第二個HPROF 文件而後重作步驟2和3.

E)切換到Compare Basket view, 而後點擊Compare the Results (視圖右上角的紅色」!」圖標)。

F)分析對比結果

能夠看出兩個hprof的數據對象對比結果。
經過這種方式能夠快速定位到操做先後所持有的對象增量,從而進一步定位出當前操做致使內存泄露的具體緣由是泄露了什麼數據對象。

注意:

若是是用 MAT Eclipse 插件獲取的 Dump文件,不須要通過轉換則可在MAT中打開,Adt會自動進行轉換。

而手機SDk Dump 出的文件要通過轉換才能被 MAT識別,Android SDK提供了這個工具 hprof-conv (位於 sdk/tools下)

首先,要經過控制檯進入到你的 android sdk tools 目錄下執行如下命令:
./hprof-conv xxx-a.hprof xxx-b.hprof
例如 hprof-conv input.hprof out.hprof
此時才能將out.hprof放在eclipse的MAT中打開。


手機管家內存泄露每日監控方案

目前手機管家的內存泄露每日監控會自動運行並輸出是否存在疑似泄露的報告郵件,不論泄露對象的大小。這其中涉及的核心技術主要是AspectJ,MLD自研工具(原理是虛引用)和UIAutomator。

3.1 AspectJ插樁監控代碼

手機管家目前使用一個ant腳本加入MLD的監控代碼,並經過AspectJ的語法實現插樁。
使用AspectJ的緣由是能夠靈活分離出項目源碼與監控代碼,經過不一樣的編譯腳本打包出不一樣用途的安裝測試包:若是測試包是通過Aspect插樁了MLD監控代碼的話,那麼運行完畢後會輸出指定格式的日誌文件,做爲後續分析工做的數據基礎。

3.2 MLD實現監控核心邏輯

這是手機管家內的一個工具工程,正式打包不會打入,BVT等每日監控測試包能夠打入。打入後能夠經過諸如addObject接口(經過反射去檢查是否含有該工具並調用)來加入須要監控的檢測對象,這個工具會自動在指定時機(如退出管家)去檢測該對象是否發生泄漏。

這個內存泄露檢測的基本原理是:

虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用必須和引用隊列(ReferenceQueue)聯合使用(在虛引用函數就必須關聯指定)。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,自動把這個虛引用加入到與之關聯的引用隊列中。程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。

基於以上原理,MLD工具在調用接口addObject加入監控類型時,會爲該類型對象增長一個虛引用,注意虛引用並不會影響該對象被正常回收。所以能夠在ReferenceQueue引用隊列中統計未被回收的監控對象是否超過指定閥值。

利用PhantomReferences(虛引用)和ReferenceQueue(引用隊列),當PhantomReferences被加入到相關聯的ReferenceQueue時,則視該對象已經或處於垃圾回收器回收階段了。

MLD監控原理核心

目前手機管家已對大部分類完成內存泄露的監控,包括各類activity,service和view頁面等,務求在技術上能帶給用戶最順滑的產品體驗。

接下來簡單介紹下這個工具的判斷核心。根據虛引用監控到的內存狀態,須要經過多種策略來判斷是否存在內存泄露。

(1)最簡單的方式就是直接在加入監控時就爲該類型設定最大存在個數,舉個例子,各個DAO對象理論上只能存在最多一個,所以一旦出現兩個相同的DAO,那通常都是泄露了;

(2)第二種狀況是在頁面退出程序退出時,檢索gc後沒法釋放的對象列表,這些對象類型也會成爲內存泄露的懷疑對象;

(3)最後一種狀況比較複雜,基本原理是根據歷史操做判斷對象數量的增加幅度。根據對象的增加經過最小二乘法擬合出該對象類型的增加速度,若是超過經驗值則會列入疑似泄露的對象列表。

3.3 UIAutomator完成重複操做的自動化

最後一步就很簡單了。這麼多反覆的UI操做,讓人工來點就太浪費人力了。咱們使用UIAutomator來進行自動化操做測試。

目前手機管家的每日自動化測試已覆蓋各個功能的主路徑,並經過配置文件的方式來靈活驅動用例的增刪改查,最大限度保證了隨着版本推移用例的複用價值。

至此手機管家的內存泄露測試方案介紹完畢,也歡迎各路牛人交流溝通更多更強的內存泄露工具盒方案!


騰訊Bugly簡介

Bugly是騰訊內部產品質量監控平臺的外發版本,其主要功能是App發佈之後,對用戶側發生的Crash以及卡頓現象進行監控並上報,讓開發同窗能夠第一時間瞭解到App的質量狀況,及時機型修改。目前騰訊內部全部的產品,均在使用其進行線上產品的崩潰監控。

相關文章
相關標籤/搜索