Android性能優化第(二)篇---Memory Monitor檢測內存泄露

版權聲明:本文為LooperJing原創文章,轉載請注明出處!

多練習多寫代碼.jpg

上篇說了一些性能優化的理論部分,主要是回顧一下,有了理論,小平同志又講了,實踐是檢驗真理的唯一標準,對于內存泄露的問題,現在通過Android Studio自帶工具Memory Monitor 檢測出來。性能優化的重要性不需要在強調,但是要強調一下,我并不是一個老司機,嘿嘿!沒用過這個工具的,請睜大眼睛。如果你用過,那么就不用在看這篇博客了。

先看一段會發生內存泄露的代碼

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);
    }
}

代碼很簡單,就是一個單利模式泄露的場景,我們現在的關心的不是代碼本身,而是如何將代碼里面的內存泄露給找出來。但是對于上面的代碼發生內存泄露的原因還是有必要提一下。

上篇博客說了,內存泄漏產生的原因是:當一個對象已經不需要再使用了,本該被回收時,而有另外一個正在使用的對象持有它的引用從而就導致,對象不能被回收。這種導致了本該被回收的對象不能被回收而停留在堆內存中,就產生了內存泄漏。

在上面的代碼中,發生泄露的不是UserManger,而是MainActivity,UserManger中有一個靜態成員instance,其生命周期和應用程序的生命周期一致,當退出應用時,才能被銷毀,但是當GC準備回收MainActivity時,結果呢MainActivity的對象(this)在被UserManger所引用,UserManger本身又不能被干掉,所以就發生了內存泄露。

monitors.png

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

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

回到我們的程序,多點擊幾次GC,看一下這個應用的內存使用情況。

內存使用情況.jpg

可以看到現在已經分配的內存有19.68M,我把手機旋轉一下,在看。


旋轉后內存使用情況.png

可以看到現在的內存使用量是21.09M,還是一樣的界面,卻多了1.41M!!!這很關鍵。

接下來,我們找一下,哪里發生了泄露。點擊Dump Java Heap,生成快照文件tool.test.memory.memoryleak_2016.11.13_21.38.hprof,Android Studio 自動彈出HPROF Viewer來分析它。

快照文件分析.png

現在介紹一下HPROF Viewer的用法

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

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

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

B板塊右上角有個"的按鈕, 點擊會進入HPROF Analyzer的hprof的分析界面:


Analyzer Tasks.png

"
在這個界面中可以直接把內存泄露可能的類找出來。

下面分析一下MainActivity的泄露情況
MainActivity發生內存泄露.png
  • 一個Activity應該只有一個實例,但是從A區域來看 total count的值為2,heap count的值也為2,說明有一個是多余的。
  • 在B區域中可以看見兩個MainActivity的實例,點擊一個看他的引用樹情況
  • 在C區域中可以看到MainActivity的實例Context被UserManger的 instance引用了,引用深度為1.
  • 在Analyzer Tasks 區域中,直接告訴你Leaked Activities,MainActivity包含其中

多方面的證據表明MainActivity發生了內存泄露

解決方案

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,因為Activity隨時可能被回收,我們用Application的Context,Application的Context的生命周期是整個應用,不回收也沒有關系。

Memory Monitor獲得內存的動態視圖,Heap Viewer顯示堆內存中存儲了什么,可惜Heap Viewer不能顯示你的數據具體分配在代碼的何處,如果還不過癮,想知道具體是哪些代碼使用了內存,還有一個功能是Allocation Tracker,用來內存分配追蹤。在內存圖中點擊途中標紅的部分,啟動追蹤,再次點擊就是停止追蹤,隨后自動生成一個alloc結尾的文件,這個文件就記錄了這次追蹤到的所有數據,然后會在右上角打開一個數據面板
Allocation Tracker啟動追蹤

Allocation Tracker啟動追蹤.png

Allocation Tracker查看方式

Allocation Tracker查看方式

有兩種查看方式,默認是Group by Method方式

  • Group by Method:用方法來分類我們的內存分配
  • Group by Allocator:用內存分配器來分類我們的內存分配

從上圖可以看出,首先以線程對象分類,Size是內存大小,Count是分配了多少次內存,點擊一下線程就會查看每個線程里所有分配內存的方法

  • Group by Method方式

    每個線程里所有分配內存的方法.png

    OK,-Memory Monitor

  • ** Group by Allocator方式**


    EY%HY_B74%BUE22C6$G~CTP.png

    右鍵可以直接跳到源碼

- 扇形統計圖

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

點擊統計圖按鈕,會生成上圖,扇形統計圖是以圓心為起點,最外層是其內存實際分配的對象,每一個同心圓可能被分割成多個部分,代表了其不同的子孫,每一個同心圓代表他的一個后代,每個分割的部分代表了某一帶人有多人,你雙擊某個同心圓中某個分割的部分,會變成以你點擊的那一代為圓心再向外展開。

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容