性能優化-內存分析工具的使用(整理)

前文講到了內存泄漏的緣由,那麼要怎麼定位內存泄漏呢?這裏列出了經常使用的分析工具及其使用方法
如下Heap SnapshotMATHeap ViewerAllaction TrackingLeakCanaryTraceView資料均來源於網絡php

Heap Snapshot

獲取Java堆內存詳細信息,能夠分析出內存泄漏的問題
在2.X版本中,Android Studio使用的分析工具
點擊Monitor即可查看CPUMemoryNetworkGPU的狀況
Heap Dump啓動
其打開面板以下:
Heap Snapshot控制面板
該面板裏的信息能夠有三種類型:app heap/image heap/zygote heap
分別表明app堆內存信息,圖片堆內存信息,zygote進程的堆內存信息
Heap Snapshot控制面板詳細信息java

A區域

列舉了堆內存中全部的類,一下是列表中列名:android

名稱 意義
Total Count 內存中該類的對象個數
Heap Count 堆內存中該類的對象個數
Sizeof 物理大小
Shallow size 該對象自己佔有內存大小
Retained Size 釋放該對象後,節省的內存大小

B區域

當咱們點擊某個類時,右邊的B區域會顯示該類的實例化對象,這裏面會顯示有多少個實體,以及詳細信息
Heap Snapshot B區域git

名稱 意義
depth 深度
Shallow Size 對象自己內存大小
Dominating Size 管轄的內存大小

當你點擊某個對象時,將展開該對象內部含有哪些對象,同時C區域也會顯示哪些對象引用了該對象github

C區域

Heap Snapshot C區域
點擊查看
Heap Snapshot C展開區域
某對象引用樹對象,在這裏面能看出其沒誰引用了,好比在內存泄漏中,能夠看出來它被誰引用,好比上圖,引用樹的第一行,能夠看出來,該對象被Object[12]對象引用,索引值爲1,那咱們展開後,能夠看到,該Object[12]是一個ArrayListweb

在3.X版本,Android Studio採用了新的分析工具,但其使用都是相似的
其啓動界面以下
Android Profiler
分析界面以下
Android Profiler具體分析界面數組

MAT

下載:http://eclipse.org/mat/downloads.php
MAT工具全稱爲Memory Analyzer Tool,一款詳細分析Java堆內存的工具,該工具很是強大,爲了使用該工具,咱們須要hprof文件。可是該文件不能直接被MAT使用,須要進行一步轉化,可使用hprof-conv命令來轉化,可是Android Studio能夠直接轉化,轉化方法以下
選擇一個hprof文件,點擊右鍵選擇Export to standard .hprof選項
MAT Android轉化1
MAT工具所需的文件就生成了,下面咱們用MAT來打開該工具:安全

  1. 打開MAT後選擇File -> Open File選擇咱們剛纔生成的hprof文件
  2. 選擇該文件後,MAT會有幾秒種的時間解析該文件,有的hprof文件可能過大,會有更長的時間解析,解析後,展示在咱們的面前的界面以下
    MAT 界面
    這是個總覽界面,會大致給出一些分析後初步的結論
  • Overview視圖
    該視圖會首頁總結出當前這個Heap dump佔用了多大的內存,其中涉及的類有多少,對象有多少,類加載器,若是有沒有回收的對象,會有一個鏈接,能夠直接參看(圖中的Unreachable Objects Histogram)。
    好比該例子中顯示了Heap dump佔用了41M的內存,5400個類,96700個對象,6個類加載器。
    而後還會有各類分類信息:
    • Biggest Objects by Retained Size
      會列舉出Retained Size值最大的幾個值,你能夠將鼠標放到餅圖中的扇葉上,能夠在右側看出詳細信息,在這裏能夠找到咱們關心的內容
    • histogram視圖
      histogram視圖主要是查看某個類的實例個數,好比咱們在檢查內存泄漏時候,要判斷是否頻繁建立了對象,就能夠來看對象的個數來看。也能夠經過排序看出佔用內存大的對象,默認是類名形式展現,也能夠選擇不一樣的顯示方式
    • Dominator tree視圖
      該視圖會以佔用總內存的百分比來列舉全部實例對象,注意這個地方是對象而不是類了,這個視圖是用來發現大內存對象的。這些對象均可以展開查看更詳細的信息,能夠看到該對象內部包含的對象
    • Leaks suspects視圖
      這個視圖會展現一些可能的內存泄漏的點

Navigation History中能夠選擇Histogram,而後右鍵加入對比,實現多個histogram數據的對比結果,從而分析內存泄漏的可能性網絡

Heap Viewer

實時查看App分配的內存大小和空閒內存大小
發現Memory Leaksapp

  • 使用條件
    5.0以上的系統,包括5.0
    開發者選項可用

在2.x的Android Studio中,
能夠直接在Android studio工具欄中直接點擊小機器人啓動
還能夠在Android studio的菜單欄中Tools
或者是在sdk的tools工具下打開
在3.x的IDE中,默認已經找不到啓動圖標,但在tools目錄下依舊能夠打開使用

Heap Viewer面板以下
Heap Viewer面板
按上圖的標記順序按下,咱們就能看到內存的具體數據,右邊面板中數值會在每次GC時發生改變,包括App自動觸發或者你來手動觸發
總覽:
Heap Viewer面板總覽

列名 意義
Heap Size 堆棧分配給App的內存大小
Allocated 已分配使用的內存大小
Free 空閒的內存大小
%Used Allocated/Heap Size,使用率
Objects 對象數量

詳情:
Heap Viewer詳情

類型 意義
free 空閒的對象
data object 數據對象,類類型對象,最主要的觀察對象
class object 類類型的引用對象
1-byte array(byte[],boolean[]) 一個字節的數組對象
2-byte array(short[],char[]) 兩個字節的數組對象
4-byte array(long[],double[]) 4個字節的數組對象
non-Java object 非Java對象

下面是每個對象都有的列名含義

列名 意義
Count 數量
Total Size 總共佔用的內存大小
Smallest 將對象佔用內存的大小從小往大排,排在第一個的對象佔用內存大小
Largest 將對象佔用內存的大小從小往大排,排在最後一個的對象佔用的內存大小
Median 將對象佔用內存的大小從小往大排,拍在中間的對象佔用的內存大小
Average 平均值

當咱們點擊某一行時,能夠看到以下的柱狀圖
Heap Viewer柱狀圖
橫座標是對象的內存大小,這些值隨着不一樣對象是不一樣的,縱座標是在某個內存大小上的對象的數量

使用:在須要檢測內存泄漏的用例執行事後,手動GC下,而後觀察data object一欄的total size(也能夠觀察Heap Size/Allocated內存的狀況),看看內存是否是會回到一個穩定值,屢次操做後,只要內存是穩定在某個值,那麼說明沒有內存溢出的,若是發現內存在每次GC後,都在增加,不論是慢增加仍是快速增加,都說明有內存泄漏的可能性

Allaction Tracking

追蹤內存分配信息。能夠很直觀地看到某個操做的內存是如何進行一步一步地分配的
Allocation Tracker(AS)工具比Allocation Tracker(Eclipse)工具強大的地方是更炫酷,更清晰,可是能作的事情都是同樣的

Allocation Tracker啓動
Allocation Tracker啓動
在內存圖中點擊途中標紅的部分,啓動追蹤,再次點擊就是中止追蹤,隨後自動生成一個alloc結尾的文件,這個文件就記錄了此次追蹤到的全部數據,而後會在右上角打開一個數據面板
Allocation Tracker數據面板
面板左上角是全部歷史數據文件列表,後面是詳細信息,好,如今咱們來看詳細介紹信息面板
Allocation Tracker詳細介紹信息面板
下面咱們用字母來分段介紹

  • A:查看方式選項
    A標識的是一個選擇框,有2個選項
    Allocation TrackerA標識
    Group by Method:用方法來分類咱們的內存分配
    Group by Allocator:用內存分配器來分類咱們的內存分配
    不一樣的選項,在D區顯示的信息會不一樣,默認會以Group by Method來組織,咱們來看看詳細信息:
    Allocation TrackerA標識信息
    從上圖能夠看出,首先以線程對象分類,默認以分配順序來排序,固然你能夠更改,只需在Size上點擊一下就會倒序,若是以Count排序也是同樣,Size就是內存大小,Count就是分配了多少次內存,點擊一下線程就會查看每一個線程裏全部分配內存的方法,而且能夠一步一步迭代到最底部
    Allocation TrackerA標識信息底部
    當你以Group by Allocator來查看內存分配的狀況時,詳細信息區域就會變成以下
    Allocation TrackerA標識信息底部詳細信息
    默認仍是之內存分配順序來排序,可是是以每一個分配者第一次分配內存的順序
    Allocation TrackerA標識信息內存分配順序
    這種方式顯示的好處,是咱們很好的定位咱們本身的代碼的分析信息,好比上圖中,以包名來找到咱們的程序,在此次追蹤中包民根目錄一共有五個類做爲分配器分配了78-4-1=73次內存
  • B:Jump To Source按鈕
    若是咱們想看內存分配的實際在源碼中發生的地方,能夠選擇須要跳轉的對象,點擊該按鈕就能發現咱們的源碼,可是前提是你有源碼
    Allocation TrackerB標識
    若是你能跳轉到源碼,Jump To Source按鈕纔是可用的,都是跳轉到類
  • C:統計圖標按鈕
    該按鈕比較酷炫,若是點擊該按鈕,會彈出一個新窗口,裏面是一個酷炫的統計圖標,有柱狀圖和輪胎圖兩種圖形可供選擇,默認是輪胎圖,其中分配比例能夠選擇分配次數和佔用內存大小,默認是大小Size
  • 輪胎圖
    Allocation TrackerC標識輪胎圖
    輪胎圖是以圓心爲起點,最外層是其內存實際分配的對象,每個同心圓可能被分割成多個部分,表明了其不一樣的子孫,每個同心圓表明他的一個後代,每一個分割的部分表明了某一帶人有多人,你雙擊某個同心圓中某個分割的部分,會變成以你點擊的那一代爲圓心再向外展開。若是想回到原始狀態,雙擊圓心就能夠了。
    1.起點
    Allocation TrackerC標識輪胎圖起點
    圓心是咱們的起點處,若是你把鼠標放到我圖中標註的區域,會在右邊顯示當前指示的是什麼線程(Thread1)以及具體信息(分配了8821次,分配了564.18k的內存),可是紅框標註的區域並不表明Thread1,而是第一個同心圓中佔比最大的那個線程,因此咱們如今把鼠標放到第一個同心圓上,能夠看出來,咱們劃過同心圓的軌跡時能夠看到右邊的樹枝變化了好幾個值
    Allocation TrackerC標識輪胎圖起點信息
    2.查看某一個扇面
    咱們剛打開是全局信息,咱們若是想看其中某個線程,詳細信息,能夠順着某個扇面向外圍滑動,固然若是你以爲不仍是不清晰,能夠雙擊該扇面全面展示該扇面的信息
    Allocation TrackerC標識輪胎圖查看信息
    在某個地方雙擊時,新的輪胎圖是以雙擊點爲圓心,你若是想到剛纔的圓,雙擊圓心空白處就能夠
    Allocation TrackerC標識輪胎圖雙擊查看
    3.一個內存的完整路徑
    Allocation TrackerC標識輪胎圖內存完整路徑
  • 柱狀圖
    Allocation TrackerC標識柱狀圖
    柱狀圖以左邊爲起始點,從左到右的順序是某個的堆棧信息順序,縱座標上的寬度是以其Count/Size的大小決定的。柱狀圖的內容其實和輪胎圖沒什麼特別的地方
    1.起點
    Allocation TrackerC標識柱狀圖
    2.查看某一個分支
    Allocation TrackerC標識柱狀圖查看分支
    3.Count/Size切換
    Allocation TrackerC標識柱狀圖切換

LeakCanary

能夠直接在手機端查看內存泄露的工具
實現原理:本質上仍是用命令控制生成hprof文件分析檢查內存泄露

添加LeakCanary依賴包

https://github.com/square/leakcanary
在主模塊app下的build.gradle下添加以下依賴

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'

LeakCanary添加依賴

開啓LeakCanary

添加Application子類
首先建立一個ExampleApplication,該類繼承於Application,在該類的onCreate方法中添加以下代碼開啓LeakCanary監控:
LeakCanary.install(this);
LeakCanary添加Application子類

在配置文件中註冊ExampleApplication

AndroidManifest.xml中的application標籤中添加以下信息:
android:name=".ExampleApplication"
LeakCanary註冊ExampleApplication

這個時候安裝應用到手機,會自動安裝一個Leaks應用,以下圖
LeakCanary安裝信息

製造一個內存泄漏的點

創建一個ActivityManager類,單例模式,裏面有一個數組用來保存Activity:

package com.example.android.sunshine.app;
import android.app.Activity;
import android.util.SparseArray;
import android.view.animation.AccelerateInterpolator;
import java.util.List;
public class ActivityManager {
    private SparseArray<Activity> container = new SparseArray<Activity>();
    private int key = 0;
    private static ActivityManager mInstance;
    private ActivityManager(){}
    public static ActivityManager getInstance(){
        if(mInstance == null){
            mInstance = new ActivityManager();
        }
        return mInstance;
    }

    public void addActivity(Activity activity){
        container.put(key++,activity);
    }
}

而後在DetailActivity中的onCreate方法中將當前activity添加到ActivityManager的數組中:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_detail);
    ActivityManager.getInstance().addActivity(this);
    if (savedInstanceState == null) {
        // Create the detail fragment and add it to the activity
        // using a fragment transaction.

        Bundle arguments = new Bundle();
        arguments.putParcelable(DetailFragment.DETAIL_URI, getIntent().getData());

        DetailFragment fragment = new DetailFragment();
        fragment.setArguments(arguments);

        getSupportFragmentManager().beginTransaction()
                .add(R.id.weather_detail_container, fragment)
                .commit();
    }
}

咱們從首頁跳轉到詳情頁的時候會進入DetailActivityonCreate的方法,而後就將當前activity添加到了數組中,當返回時,咱們沒把他從數組中刪除。再次進入的時候,會建立新的activity並添加到數組中,可是以前的activity仍然被引用,沒法釋放,可是這個activity不會再被使用,這個時候就形成了內存泄漏。咱們來看看LeakCanary是如何報出這個問題的

演示

LeakCanary演示
解析的過程有點耗時,因此須要等待一會纔會在Leaks應用中,當咱們點開某一個信息時,會看到詳細的泄漏信息
LeakCanary演示1

TraceView

從代碼層面分析性能問題,針對每一個方法來分析,好比當咱們發現咱們的應用出現卡頓的時候,咱們能夠來分析出現卡頓時在方法的調用上有沒有很耗時的操做,關注如下兩個問題:

  • 調用次數很少,可是每一次執行都很耗時
  • 方法耗時不大,可是調用次數太多
    簡單一點來講就是咱們能找到頻繁被調用的方法,也能找到執行很是耗時的方法,前者可能會形成cpu頻繁調用,手機發燙的問題,後者就是卡頓的問題

TraceView工具啓動

打開Monitor,點擊圖中的標註的按鈕,啓動追蹤
TraceView工具啓動

TraceView工具面板

打開App操做你的應用後,再次點擊的話就中止追蹤而且自動打開traceview分析面板
TraceView工具面板
traceview的面板分上下兩個部分:

  • 時間線面板以每一個線程爲一行,右邊是該線程在整個過程當中方法執行的狀況
  • 分析面板是以表格的形式展現全部線程的方法的各項指標

時間線面板

TraceView時間線面板
左邊是線程信息,main線程就是Android應用的主線程,這個線程是都會有的,其餘的線程可能因操做不一樣而發生改變.每一個線程的右邊對應的是該線程中每一個方法的執行信息,左邊爲第一個方法執行開始,最右邊爲最後一個方法執行結束,其中的每個小立柱就表明一次方法的調用,你能夠把鼠標放到立柱上,就會顯示該方法調用的詳細信息
TraceView顯示方法調用
你能夠隨意滑動你的鼠標,滑倒哪裏,左上角就會顯示該方法調用的信息。
1.若是你想在分析面板中詳細查看該方法,能夠雙擊該立柱,分析面板自動跳轉到該方法
TraceView顯示詳細信息
2.放大某個區域
剛打開的面板中,是咱們採集信息的總覽,可是一些局部的細節咱們看不太清,不要緊,該工具支持咱們放大某個特殊的時間段
TraceView放大某個區域
若是想回到最初的狀態,雙擊時間線就能夠
3.每個方法的表示
TraceView方法的表示
能夠看出來,每個方法都是用一個凹型結構來表示,座標的凸起部分表示方法的開始,右邊的凸起部分表示方法的結束,中間的直線表示方法的持續

分析面板

面板列名含義以下

名稱 意義
Name 方法的詳細信息,包括包名和參數信息
Incl Cpu Time Cpu執行該方法該方法及其子方法所花費的時間
Incl Cpu Time % Cpu執行該方法該方法及其子方法所花費佔Cpu總執行時間的百分比
Excl Cpu Time Cpu執行該方法所話費的時間
Excl Cpu Time % Cpu執行該方法所話費的時間佔Cpu總時間的百分比
Incl Real Time 該方法及其子方法執行所話費的實際時間,從執行該方法到結束一共花了多少時間
Incl Real Time % 上述時間佔總的運行時間的百分比
Excl Real Time % 該方法自身的實際容許時間
Excl Real Time 上述時間佔總的容許時間的百分比
Calls+Recur 調用次數+遞歸次數,只在方法中顯示,在子展開後的父類和子類方法這一欄被下面的數據代替
Calls/Total 調用次數和總次數的佔比
Cpu Time/Call Cpu執行時間和調用次數的百分比,表明該函數消耗cpu的平均時間
Real Time/Call 實際時間於調用次數的百分比,該表該函數平均執行時間

你能夠點擊某個函數展開更詳細的信息
TraceView函數展開
展開後,大多數有如下兩個類別:

  • Parents:調用該方法的父類方法
  • Children:該方法調用的子類方法

若是該方法含有遞歸調用,可能還會多出兩個類別:

  • Parents while recursive:遞歸調用時所涉及的父類方法
  • Children while recursive:遞歸調用時所涉及的子類方法

首先咱們來看當前方法的信息

Name 24 android/widget/FrameLayout.draw(L android/graphics/Canvas;)V
Incl Cpu% 20.9%
Incl Cpu Time 375.201
Excl Cpu Time % 0.0%
Excl Cpu Time 0.000
Incl Real Time % 1.1%
Incl Real Time 580.668
Excl Real Time % 0.0%
Excl Real Time 0.000
Calls+Recur 177+354
Cpu Time/Call 0.707
Real Time/Call 1.094

根據下圖中的toplevel能夠看出總的cpu執行時間爲1797.167ms,當前方法佔用cpu的時間爲375.201375.201/1797.167=0.2087,和咱們的Incl Cpu Time%是吻合的。當前方法消耗的時間爲580.668,而toplevel的時間爲53844.141ms,580.668/53844.141=1.07%,和Incl Real Time %也是吻合的。在來看調用次數爲177,遞歸次數爲354,和爲177+354=531375.201/531 = 0.7065Cpu Time/Call也是吻合的,580.668/531=1.0935,和Real Time/Call一欄也是吻合的
TraceView計算

  • Parents
    如今咱們來看該方法的Parents一欄
    TraceViewParents
Name 22 com/android/internal/policy/impl/PhoneWindow$DecorView.draw(Landroid/graphics/Canvas;)V
Incl Cpu% 100%
Incl Cpu Time 375.201
Excl Cpu Time %
Excl Cpu Time
Incl Real Time % 100%
Incl Real Time 580.668
Excl Real Time %
Excl Real Time
Call/Total 177/531
Cpu Time/Call
Real Time/Call

其中的Incl Cpu Time%變成了100%,由於在這個地方,總時間爲當前方法的執行時間,這個時候的Incl Cpu Time%只是計算該方法調用的總時間中被各父類方法調用的時間佔比,好比Parents有2個父類方法,那就能看出每一個父類方法調用該方法的時間分佈。由於咱們父類只有一個,因此確定是100%Incl Real Time一欄也是同樣的,重點是Call/Total,以前咱們看當前方式時,這一欄的列名爲Call+Recur,而如今變成了Call/Total,這個裏面的數值變成了177/531,由於總次數爲531次,父類調用了177次,其餘531次是遞歸調用。這一數據能獲得的信息是,當前方法被調用了多少次,其中有多少次是父類方法調用的

  • Children
    TraceViewChildren
    能夠看出來,咱們的子類有2個,一個是自身,一個是23android/view/View.draw(L android/graphics/Canvas;)Vself表明自身方法中的語句執行狀況,由上面能夠看出來,該方法沒有多餘語句,直接調用了其子類方法。另一個子類方法,能夠看出被當前方法調用了177次,可是該方法被其餘方法調用過,由於他的總調用次數爲892次,你能夠點擊進入子類方法的詳細信息中

  • Parents while recursive
    TraceViewParents while recursive
    列舉了遞歸調用當前方法的父類方法,以及其遞歸次數的佔比,猶豫咱們當前的方法遞歸了354次,以上三個父類方法遞歸的次數分別爲348+4+2=354

  • Children while recursive
    TraceViewChildren while recursive
    列舉了當遞歸調用時調用的子類方法

Lint分析工具

檢測資源文件是否有沒有用到的資源。
檢測常見內存泄露
安全問題
SDK版本安全問題
是否有費的代碼沒有用到
代碼的規範—甚至駝峯命名法也會檢測
自動生成的羅列出來
沒用的導包
可能的bug

Analyze -> Inspect Code即可執行檢查
能夠檢查project,Module和指定文件
Link分析工具
詳細信息
Link分析工具分析界面

相關文章
相關標籤/搜索