Android性能優(yōu)化第(二)篇---Memory Monitor檢測(cè)內(nèi)存泄露

版權(quán)聲明:本文為LooperJing原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處!

多練習(xí)多寫代碼.jpg

上篇說(shuō)了一些性能優(yōu)化的理論部分,主要是回顧一下,有了理論,小平同志又講了,實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),對(duì)于內(nèi)存泄露的問(wèn)題,現(xiàn)在通過(guò)Android Studio自帶工具M(jìn)emory Monitor 檢測(cè)出來(lái)。性能優(yōu)化的重要性不需要在強(qiáng)調(diào),但是要強(qiáng)調(diào)一下,我并不是一個(gè)老司機(jī),嘿嘿!沒(méi)用過(guò)這個(gè)工具的,請(qǐng)睜大眼睛。如果你用過(guò),那么就不用在看這篇博客了。

先看一段會(huì)發(fā)生內(nèi)存泄露的代碼

public class UserManger {

    private static UserManger instance;

    private Context context;

    private UserManger(Context context) {
        this.context = context;
    }

    public static UserManger getInstance(Context context) {
        if (instance == null) {
            instance = new UserManger(context);
        }
        return instance;
    }
}

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        UserManger userManger = UserManger.getInstance(this);
    }
}

代碼很簡(jiǎn)單,就是一個(gè)單利模式泄露的場(chǎng)景,我們現(xiàn)在的關(guān)心的不是代碼本身,而是如何將代碼里面的內(nèi)存泄露給找出來(lái)。但是對(duì)于上面的代碼發(fā)生內(nèi)存泄露的原因還是有必要提一下。

上篇博客說(shuō)了,內(nèi)存泄漏產(chǎn)生的原因是:當(dāng)一個(gè)對(duì)象已經(jīng)不需要再使用了,本該被回收時(shí),而有另外一個(gè)正在使用的對(duì)象持有它的引用從而就導(dǎo)致,對(duì)象不能被回收。這種導(dǎo)致了本該被回收的對(duì)象不能被回收而停留在堆內(nèi)存中,就產(chǎn)生了內(nèi)存泄漏。

在上面的代碼中,發(fā)生泄露的不是UserManger,而是MainActivity,UserManger中有一個(gè)靜態(tài)成員instance,其生命周期和應(yīng)用程序的生命周期一致,當(dāng)退出應(yīng)用時(shí),才能被銷毀,但是當(dāng)GC準(zhǔn)備回收MainActivity時(shí),結(jié)果呢MainActivity的對(duì)象(this)在被UserManger所引用,UserManger本身又不能被干掉,所以就發(fā)生了內(nèi)存泄露。

monitors.png

Memory Monitor是Android Monitors中的一種,Monitors主要包括四種,Memory Monitor ,CPU Monitor ,NetWork Monitor, GPU Monitor ,今天介紹的是Memory Monitor ,其他的Monitor,在后面也準(zhǔn)備講。

  • Memory Monitor界面
Memory Monitor.png
  • 圖中水平方向是時(shí)間軸,豎直方向是內(nèi)存的分配情況
  • 圖中深藍(lán)色的區(qū)域,表示當(dāng)前正在使用中的內(nèi)存總量,淺藍(lán)色或者淺灰色區(qū)域,表示空閑內(nèi)存或者叫作未分配內(nèi)存。
  • 左上角工具欄三個(gè)圓圈按鈕依次代表
    GC按鈕 ,可以手動(dòng)GC,回收程序垃圾
    內(nèi)存快照(Dump Java Heap) ,點(diǎn)擊可以生成一個(gè)文件(包名+日期+“.hprof”),可以記錄摸一個(gè)時(shí)間點(diǎn)內(nèi),程序內(nèi)存的情況
    Allocation Traking ,點(diǎn)擊一次開(kāi)始, 再次點(diǎn)擊結(jié)束,也可以可以生成一個(gè)文件。

回到我們的程序,多點(diǎn)擊幾次GC,看一下這個(gè)應(yīng)用的內(nèi)存使用情況。

內(nèi)存使用情況.jpg

可以看到現(xiàn)在已經(jīng)分配的內(nèi)存有19.68M,我把手機(jī)旋轉(zhuǎn)一下,在看。


旋轉(zhuǎn)后內(nèi)存使用情況.png

可以看到現(xiàn)在的內(nèi)存使用量是21.09M,還是一樣的界面,卻多了1.41M!!!這很關(guān)鍵。

接下來(lái),我們找一下,哪里發(fā)生了泄露。點(diǎn)擊Dump Java Heap,生成快照文件tool.test.memory.memoryleak_2016.11.13_21.38.hprof,Android Studio 自動(dòng)彈出HPROF Viewer來(lái)分析它。

快照文件分析.png

現(xiàn)在介紹一下HPROF Viewer的用法

  • HPROF Viewer查看方式
    左上角兩個(gè)紅框,是可選列表, 分別是用來(lái)選擇Heap區(qū)域, 和Class View的展示方式的.
    Heap類型分為:
    App Heap -- 當(dāng)前App使用的Heap
    Image Heap -- 磁盤上當(dāng)前App的內(nèi)存映射拷貝
    Zygote Heap -- Zygote進(jìn)程Heap(每個(gè)App進(jìn)程都是從Zygote孵化出來(lái)的, 這部分基本是framework中的通用的類的Heap)
    Class List View -- 類列表方式
    Package Tree View -- 根據(jù)包結(jié)構(gòu)的樹(shù)狀顯示

我通常點(diǎn)擊App heap下面的Classs Name把Heap中所有類按照字母順序排序,然后按照字母順序查找。

  • HPROF Viewer主要分ABC三大板塊
    板塊A:這個(gè)應(yīng)用中所有類的名字
    版塊B:左邊類的所有實(shí)例
    板塊C:在選擇B中的實(shí)例后,這個(gè)實(shí)例的引用樹(shù)
  • A板塊左上角列名解釋
列名 解釋
Class Name 類名,Heap中的所有Class
Total Count 內(nèi)存中該類這個(gè)對(duì)象總共的數(shù)量,有的在棧中,有的在堆中
Heap Count 堆內(nèi)存中這個(gè)類 對(duì)象的個(gè)數(shù)
Sizeof 每個(gè)該實(shí)例占用的內(nèi)存大小
Shallow Size 所有該類的實(shí)例占用的內(nèi)存大小
Retained Size 所有該類對(duì)象被釋放掉,會(huì)釋放多少內(nèi)存
  • B板塊右上角上角列名解釋
列名 解釋
Instance 該類的實(shí)例
Depth 深度, 從任一GC Root點(diǎn)到該實(shí)例的最短跳數(shù)
Dominating Size 該實(shí)例可支配的內(nèi)存大小

B板塊右上角有個(gè)"的按鈕, 點(diǎn)擊會(huì)進(jìn)入HPROF Analyzer的hprof的分析界面:


Analyzer Tasks.png

"
在這個(gè)界面中可以直接把內(nèi)存泄露可能的類找出來(lái)。

下面分析一下MainActivity的泄露情況
MainActivity發(fā)生內(nèi)存泄露.png
  • 一個(gè)Activity應(yīng)該只有一個(gè)實(shí)例,但是從A區(qū)域來(lái)看 total count的值為2,heap count的值也為2,說(shuō)明有一個(gè)是多余的。
  • 在B區(qū)域中可以看見(jiàn)兩個(gè)MainActivity的實(shí)例,點(diǎn)擊一個(gè)看他的引用樹(shù)情況
  • 在C區(qū)域中可以看到MainActivity的實(shí)例Context被UserManger的 instance引用了,引用深度為1.
  • 在Analyzer Tasks 區(qū)域中,直接告訴你Leaked Activities,MainActivity包含其中

多方面的證據(jù)表明MainActivity發(fā)生了內(nèi)存泄露

解決方案

public class UserManger {

    private static UserManger instance;

    private Context context;

    private UserManger(Context context) {
        this.context = context;
    }

    public static UserManger getInstance(Context context) {
        if (instance == null) {
            if(context!=null){
                instance = new UserManger(context.getApplicationContext());
            }
        }
        return instance;
    }
}

不要用Activity的Context,因?yàn)锳ctivity隨時(shí)可能被回收,我們用Application的Context,Application的Context的生命周期是整個(gè)應(yīng)用,不回收也沒(méi)有關(guān)系。

Memory Monitor獲得內(nèi)存的動(dòng)態(tài)視圖,Heap Viewer顯示堆內(nèi)存中存儲(chǔ)了什么,可惜Heap Viewer不能顯示你的數(shù)據(jù)具體分配在代碼的何處,如果還不過(guò)癮,想知道具體是哪些代碼使用了內(nèi)存,還有一個(gè)功能是Allocation Tracker,用來(lái)內(nèi)存分配追蹤。在內(nèi)存圖中點(diǎn)擊途中標(biāo)紅的部分,啟動(dòng)追蹤,再次點(diǎn)擊就是停止追蹤,隨后自動(dòng)生成一個(gè)alloc結(jié)尾的文件,這個(gè)文件就記錄了這次追蹤到的所有數(shù)據(jù),然后會(huì)在右上角打開(kāi)一個(gè)數(shù)據(jù)面板
Allocation Tracker啟動(dòng)追蹤

Allocation Tracker啟動(dòng)追蹤.png

Allocation Tracker查看方式

Allocation Tracker查看方式

有兩種查看方式,默認(rèn)是Group by Method方式

  • Group by Method:用方法來(lái)分類我們的內(nèi)存分配
  • Group by Allocator:用內(nèi)存分配器來(lái)分類我們的內(nèi)存分配

從上圖可以看出,首先以線程對(duì)象分類,Size是內(nèi)存大小,Count是分配了多少次內(nèi)存,點(diǎn)擊一下線程就會(huì)查看每個(gè)線程里所有分配內(nèi)存的方法

  • Group by Method方式

    每個(gè)線程里所有分配內(nèi)存的方法.png

    OK,-Memory Monitor

  • ** Group by Allocator方式**


    EY%HY_B74%BUE22C6$G~CTP.png

    右鍵可以直接跳到源碼

- 扇形統(tǒng)計(jì)圖

AQFHT$@7TYP0S_1`DU@%S%6.png

點(diǎn)擊統(tǒng)計(jì)圖按鈕,會(huì)生成上圖,扇形統(tǒng)計(jì)圖是以圓心為起點(diǎn),最外層是其內(nèi)存實(shí)際分配的對(duì)象,每一個(gè)同心圓可能被分割成多個(gè)部分,代表了其不同的子孫,每一個(gè)同心圓代表他的一個(gè)后代,每個(gè)分割的部分代表了某一帶人有多人,你雙擊某個(gè)同心圓中某個(gè)分割的部分,會(huì)變成以你點(diǎn)擊的那一代為圓心再向外展開(kāi)。

除了扇形圖,還有柱狀圖可選擇,可以自己操作,OK,Memory Monitor到此結(jié)束,下一篇性能優(yōu)化部分博客仍然是檢測(cè)內(nèi)存泄露,明天上班,晚安!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,634評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 176,653評(píng)論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,063評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,835評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,235評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,459評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,000評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,819評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,004評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,257評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 34,676評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 35,937評(píng)論 1 288
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,717評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,003評(píng)論 2 374

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