Profiler分析內存抖動,Memory Analyzer(mat)分析內存泄漏(不懂砍我)

前言: 最近在系統性的溫習了一遍android性能優化。寫博客是學習也是記錄,希望在記錄的同時也能幫助其他同學。最近我覺得我想出一個不懂系列。“不懂揍我”,“不懂砍我”,“不懂捶我”

一、Profiler分析內存抖動

在我們開發項目中,如果稍不注意,往往會出現內存抖動的情況。而有些內存抖動也可能造成我們的程序卡頓,甚至泄漏。接下來使用Android Studio自帶的Profiler分析內存抖動。


1.1、模擬內存抖動並打開Profiler

首先在MainActivity創建一段內存抖動的代碼:

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //創造內存抖動
            for (int i = 0; i < 100; i++) {
                String arg[] = new String[100000];
            }
            mHandler.sendEmptyMessageDelayed(0, 30);
        }
    };

然後點擊下圖紅色框內按鈕運行程序


運行之後出現如下圖,


鼠標移動到MEMORY上雙擊,進入內存分析


如果你的項目已經運行,也可以通過View --> Tool Windows --> Profiler打開


1.2、分析內存抖動

點擊按鈕,觸發內存抖動的代碼,來看看我們的內存情況,可以看到呈鋸齒狀

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-It94iFlb-1589018239602)(https://user-gold-cdn.xitu.io/2020/5/9/171f780686912499?w=1613&h=558&f=png&s=124757)]

點擊上圖紅色框內record,然後點擊stop分析一段我們的鋸齒狀,如圖:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FaU9X4Ny-1589018239603)(https://user-gold-cdn.xitu.io/2020/5/9/171f7861f6544a55?w=1620&h=536&f=png&s=141052)]

Allocations: 表示當前分配的數量
Shallow Size: 表示當前內存佔用情況

可以看到我們的String[]佔用最多,左鍵點擊String[]後,右側會出現所有的String[],然後左鍵點擊任意一個,會出現Allocation Call Stack:內存分配的堆棧信息:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qG6fCUWG-1589018239606)(https://user-gold-cdn.xitu.io/2020/5/9/171f78b3a26c5377?w=1619&h=688&f=png&s=194039)]

可以很明顯看到在我們代碼裏,是因爲handleMessage:21, MainActivity$1 (com.leo.memoryanalyzertest),造成的內存抖動。右鍵這條,點擊Jump to Source.可以跳轉到我們的問題代碼。這就是我們Profiler分析內存抖動的情況。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-iUvBSwKD-1589018239607)(https://user-gold-cdn.xitu.io/2020/5/9/171f78deef594435?w=597&h=79&f=png&s=9879)]

1.3、如果用上篇的traceView方式,即CPU Profiler方式

首先我們推測下,traceView是分析卡頓利器,當然在這裏能測出。但要知道它是偏向耗時情況的。上篇說過,這裏就不說明了。同樣點擊CPU,然後record,stop記錄一段。如圖來到我們的Top Down:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6StOqYUE-1589018239608)(https://user-gold-cdn.xitu.io/2020/5/9/171f7d30377e0bf7?w=1616&h=432&f=png&s=76401)]

可以看到耗時時間在MessageQueue裏,這也正和我們的內存抖動代碼相關聯,每間隔30ms發送一次,在handleMessage()裏,也指明瞭代碼在com.leo.memoryanalyzertest.MainActivity裏。可以發現這種方式去分析,並不明顯。

注意:我們都知道內存抖動,我們要尋找的話,也是找循環或者頻繁調用的地方。所以這麼看我們用CPU Profiler的方式也能大概確認問題代碼的位置。


二、Memory Analyzer之分析內存泄漏

同樣我們先創建一段內存泄漏的代碼,先定義個接口

public interface CallBack {
    void leoFun();
}

然後創建個靜態list,添加CallBack實例

public class CallBackManager {
    public static ArrayList<CallBack> sCallBacks = new ArrayList<>();

    public static void addCallBack(CallBack callBack) {
        sCallBacks.add(callBack);
    }

    public static void removeCallBack(Callback callback) {
        sCallBacks.remove(callback);
    }
}

再創建個BitmapActivity實現CallBack接口,設置一個大圖main_bg,同時把實例假如到靜態sCallBacks中,這樣每次打開BitmapActivity然後退出,都會造成BitmapActivity不能被回收。

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bitmap);

        ImageView imageView = findViewById(R.id.image);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.main_bg);
        imageView.setImageBitmap(bitmap);
        CallBackManager.addCallBack(this);
    }

這樣我們不停打開這個頁面然後關掉看內存情況,如圖,可以看到內存直接上升:

紅色框1:功能是主動發起一次gc。目的是回收一些可回收的,虛引用等。避免內存分析干擾
紅色框2: 堆轉儲,將內存分配情況轉換成hprof文件。(注意這裏是studio的hprof文件,如果需要用Memory Analyzer分析,還要轉換成標準的hprof文件)

點擊右鍵Export,將文件存儲在文件夾裏。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UXAYTrwP-1589018239611)(https://user-gold-cdn.xitu.io/2020/5/9/171f804db306f156?w=406&h=202&f=png&s=11520)]

轉換成mat能識別的hprof文件,只要cmd命令行來到我們的studio自帶的hprof-conv.exe文件下,輸入命令,即可轉換

hprof-conv 源文件路徑 輸出文件路徑,這樣就生成了我們的my.hprof


2.1、怎麼使用我們的Memory Analyzer

生成了hprof文件後,然後就是使用Memory Analyzer。
官網下載地址:https://www.eclipse.org/mat/downloads.php,因爲捐贈什麼的,不建議。我這找到了一個比較靠譜的版本,csdn下載

下載好後啓動我們的MemoryAnalyzer.exe。File --> Open Heap Dump打開我們的my.hprof文件如圖:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VultZtAS-1589018239616)(https://user-gold-cdn.xitu.io/2020/5/9/171f88c66a73a658?w=1672&h=866&f=png&s=74494)]

先簡單介紹下Memory Analyzer的信息。

2.1.1、紅色框1: OverView大概信息

  • Size 內存大小
  • Classes : class對象
  • Objects : Objects個數,也就是實例個數
  • Unreachable Objects Histogram :可被回收的對象,扔在內存當中

2.1.2、紅色框2: Histogram直方圖

  • Objects: 這個類有多少個實例
  • Shallow Heap: 單個實例佔用的內存
  • Retained Heap: 單個實例及其引用一共佔有多少內存

我們可以利用直方圖,裏的Regex搜索我們最開始的內存泄漏案例。搜索BitmapActivity如圖,可以看到我們的BitmapActivity有3個實例:

我們可以右鍵點擊com.leo.me,oryanalyzertest.BitmapActivity --> List objects --> with incoming reference (誰引用了我),如圖可以看到有3個地方引用了BitmapActivity:

此時我們右鍵一個實例點擊 Path To GC Roots --> with all refrence,如圖,找到文件下帶小太陽的文件,可能觸發內存溢出的地方。這裏是說我們的sCallBacks引用了,也證實了在我們內存泄漏代碼裏的CallBackManager裏的靜態集合sCallBacks添加了它。


2.1.3、紅色框3:dominator tree展示每個實例的內存和所佔比例

可以看到我們這裏的大頭是bitmap。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Z2MXW4F3-1589018239623)(https://user-gold-cdn.xitu.io/2020/5/9/171f869560338e9c?w=1448&h=209&f=png&s=32203)]

同樣我們可以通過右鍵一個實例Path To GC Roots --> with all refrence如圖,也能看到是我們的sCallBacks引用了:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-YXLRYn0a-1589018239624)(https://user-gold-cdn.xitu.io/2020/5/9/171f86d44c60df13?w=716&h=387&f=png&s=43407)]


2.1.4、紅色框4:OQL,類似於數據庫搜索我們的內存情況

寫入命令後,點擊紅色感嘆號運行,如圖

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-HplpQ0dW-1589018239625)(https://user-gold-cdn.xitu.io/2020/5/9/171f870f6aa8ce57?w=670&h=350&f=png&s=25925)]


2.1.5、紅色框5:thread overview,展示線程情況


2.1.6、紅色框6:Top Consumers (重點),直接展示出我們內存中的大內存

展示我們內存中佔有比較大的內存,可以看到這裏都是被3個大bitmap佔據着。直觀利於分析


2.1.7、紅色框7:Leak Suspects(重點),直接給出分析,分析出我們代碼中可能會出現內存泄漏的地方

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0JPPBQZT-1589018239628)(https://user-gold-cdn.xitu.io/2020/5/9/171f879c46453d10?w=832&h=680&f=png&s=56188)]

這裏我啓動了3次BitmapActivity,它也給了我3個分析。點擊details。看詳情,也直接把問題定在了sCallBacks中:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-64JHGAkL-1589018239629)(https://user-gold-cdn.xitu.io/2020/5/9/171f87de82e08ede?w=873&h=445&f=png&s=55325)]


我的公衆號

會發一些大白話實用的文章。也一直在尋找最有利的面試技巧。有想法的同學可以一起

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章