android中momery检测,Android性能优化第(二)篇---Memory Monitor检测内存泄露
版權(quán)聲明:本文為LooperJing原創(chuàng)文章,轉(zhuǎn)載請注明出處!
多練習(xí)多寫代碼.jpg
上篇說了一些性能優(yōu)化的理論部分,主要是回顧一下,有了理論,小平同志又講了,實踐是檢驗真理的唯一標(biāo)準(zhǔn),對于內(nèi)存泄露的問題,現(xiàn)在通過Android Studio自帶工具M(jìn)emory Monitor 檢測出來。性能優(yōu)化的重要性不需要在強(qiáng)調(diào),但是要強(qiáng)調(diào)一下,我并不是一個老司機(jī),嘿嘿!沒用過這個工具的,請睜大眼睛。如果你用過,那么就不用在看這篇博客了。
先看一段會發(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);
}
}
代碼很簡單,就是一個單利模式泄露的場景,我們現(xiàn)在的關(guān)心的不是代碼本身,而是如何將代碼里面的內(nèi)存泄露給找出來。但是對于上面的代碼發(fā)生內(nèi)存泄露的原因還是有必要提一下。
上篇博客說了,內(nèi)存泄漏產(chǎn)生的原因是:當(dāng)一個對象已經(jīng)不需要再使用了,本該被回收時,而有另外一個正在使用的對象持有它的引用從而就導(dǎo)致,對象不能被回收。這種導(dǎo)致了本該被回收的對象不能被回收而停留在堆內(nèi)存中,就產(chǎn)生了內(nèi)存泄漏。
在上面的代碼中,發(fā)生泄露的不是UserManger,而是MainActivity,UserManger中有一個靜態(tài)成員instance,其生命周期和應(yīng)用程序的生命周期一致,當(dāng)退出應(yīng)用時,才能被銷毀,但是當(dāng)GC準(zhǔn)備回收MainActivity時,結(jié)果呢MainActivity的對象(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
圖中水平方向是時間軸,豎直方向是內(nèi)存的分配情況
圖中深藍(lán)色的區(qū)域,表示當(dāng)前正在使用中的內(nèi)存總量,淺藍(lán)色或者淺灰色區(qū)域,表示空閑內(nèi)存或者叫作未分配內(nèi)存。
左上角工具欄三個圓圈按鈕依次代表
GC按鈕 ,可以手動GC,回收程序垃圾
內(nèi)存快照(Dump Java Heap) ,點擊可以生成一個文件(包名+日期+“.hprof”),可以記錄摸一個時間點內(nèi),程序內(nèi)存的情況
Allocation Traking ,點擊一次開始, 再次點擊結(jié)束,也可以可以生成一個文件。
回到我們的程序,多點擊幾次GC,看一下這個應(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)鍵。
接下來,我們找一下,哪里發(fā)生了泄露。點擊Dump Java Heap,生成快照文件tool.test.memory.memoryleak_2016.11.13_21.38.hprof,Android Studio 自動彈出HPROF Viewer來分析它。
快照文件分析.png
現(xiàn)在介紹一下HPROF Viewer的用法
HPROF Viewer查看方式
左上角兩個紅框,是可選列表, 分別是用來選擇Heap區(qū)域, 和Class View的展示方式的.
Heap類型分為:
App Heap -- 當(dāng)前App使用的Heap
Image Heap -- 磁盤上當(dāng)前App的內(nèi)存映射拷貝
Zygote Heap -- Zygote進(jìn)程Heap(每個App進(jìn)程都是從Zygote孵化出來的, 這部分基本是framework中的通用的類的Heap)
Class List View -- 類列表方式
Package Tree View -- 根據(jù)包結(jié)構(gòu)的樹狀顯示
我通常點擊App heap下面的Classs Name把Heap中所有類按照字母順序排序,然后按照字母順序查找。
HPROF Viewer主要分ABC三大板塊
板塊A:這個應(yīng)用中所有類的名字
版塊B:左邊類的所有實例
板塊C:在選擇B中的實例后,這個實例的引用樹
A板塊左上角列名解釋
列名
解釋
Class Name
類名,Heap中的所有Class
Total Count
內(nèi)存中該類這個對象總共的數(shù)量,有的在棧中,有的在堆中
Heap Count
堆內(nèi)存中這個類 對象的個數(shù)
Sizeof
每個該實例占用的內(nèi)存大小
Shallow Size
所有該類的實例占用的內(nèi)存大小
Retained Size
所有該類對象被釋放掉,會釋放多少內(nèi)存
B板塊右上角上角列名解釋
列名
解釋
Instance
該類的實例
Depth
深度, 從任一GC Root點到該實例的最短跳數(shù)
Dominating Size
該實例可支配的內(nèi)存大小
B板塊右上角有個"的按鈕, 點擊會進(jìn)入HPROF Analyzer的hprof的分析界面:
Analyzer Tasks.png
"
在這個界面中可以直接把內(nèi)存泄露可能的類找出來。
下面分析一下MainActivity的泄露情況
MainActivity發(fā)生內(nèi)存泄露.png
一個Activity應(yīng)該只有一個實例,但是從A區(qū)域來看 total count的值為2,heap count的值也為2,說明有一個是多余的。
在B區(qū)域中可以看見兩個MainActivity的實例,點擊一個看他的引用樹情況
在C區(qū)域中可以看到MainActivity的實例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,因為Activity隨時可能被回收,我們用Application的Context,Application的Context的生命周期是整個應(yīng)用,不回收也沒有關(guān)系。
Memory Monitor獲得內(nèi)存的動態(tài)視圖,Heap Viewer顯示堆內(nèi)存中存儲了什么,可惜Heap Viewer不能顯示你的數(shù)據(jù)具體分配在代碼的何處,如果還不過癮,想知道具體是哪些代碼使用了內(nèi)存,還有一個功能是Allocation Tracker,用來內(nèi)存分配追蹤。在內(nèi)存圖中點擊途中標(biāo)紅的部分,啟動追蹤,再次點擊就是停止追蹤,隨后自動生成一個alloc結(jié)尾的文件,這個文件就記錄了這次追蹤到的所有數(shù)據(jù),然后會在右上角打開一個數(shù)據(jù)面板
Allocation Tracker啟動追蹤
Allocation Tracker啟動追蹤.png,
Allocation Tracker查看方式
Allocation Tracker查看方式
有兩種查看方式,默認(rèn)是Group by Method方式
Group by Method:用方法來分類我們的內(nèi)存分配
Group by Allocator:用內(nèi)存分配器來分類我們的內(nèi)存分配
從上圖可以看出,首先以線程對象分類,Size是內(nèi)存大小,Count是分配了多少次內(nèi)存,點擊一下線程就會查看每個線程里所有分配內(nèi)存的方法
Group by Method方式
每個線程里所有分配內(nèi)存的方法.png
OK,-Memory Monitor
** Group by Allocator方式**
EY%HY_B74%BUE22C6$G~CTP.png
右鍵可以直接跳到源碼
- 扇形統(tǒng)計圖
AQFHT$@7TYP0S_1`DU@%S%6.png
點擊統(tǒng)計圖按鈕,會生成上圖,扇形統(tǒng)計圖是以圓心為起點,最外層是其內(nèi)存實際分配的對象,每一個同心圓可能被分割成多個部分,代表了其不同的子孫,每一個同心圓代表他的一個后代,每個分割的部分代表了某一帶人有多人,你雙擊某個同心圓中某個分割的部分,會變成以你點擊的那一代為圓心再向外展開。
除了扇形圖,還有柱狀圖可選擇,可以自己操作,OK,Memory Monitor到此結(jié)束,下一篇性能優(yōu)化部分博客仍然是檢測內(nèi)存泄露,明天上班,晚安!
總結(jié)
以上是生活随笔為你收集整理的android中momery检测,Android性能优化第(二)篇---Memory Monitor检测内存泄露的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数组元素替换_Linux Shell 通
- 下一篇: 信息学奥赛一本通 1342:【例4-1】