內(nèi)存分析(in-memory analytics)是我們編寫速度快、效率高的代碼必不可少的知識(shí)。如果自己編寫的代碼在內(nèi)存的分配一無(wú)所知,我想這樣的程序讓你去優(yōu)化,應(yīng)該是無(wú)從下手的。那么內(nèi)存分析是什么?是指當(dāng)數(shù)據(jù)存放在計(jì)算機(jī)的隨機(jī)存取存儲(chǔ)器(RAM)中時(shí)查詢數(shù)據(jù)的方式,和存儲(chǔ)在物理磁盤中的數(shù)據(jù)的查詢方式相反。隨著RAM成本的下降,內(nèi)存分析對(duì)很多業(yè)務(wù)都變得可行。
Android的一些內(nèi)存知識(shí)
垃圾回收(GC)
垃圾回收包含兩個(gè)過(guò)程:
本帖隱藏的內(nèi)容
1)判定階段,也就是判斷哪些對(duì)象可以被回收,
2)收集階段,是指具體的回收策略。
判定階段主要有兩種方式
1)引用計(jì)數(shù),對(duì)象每多一個(gè)引用計(jì)數(shù)加1,少一個(gè)引用計(jì)數(shù)減1,計(jì)數(shù)為0時(shí)就表示這個(gè)對(duì)象可以被回收了。但是引用計(jì)數(shù)有個(gè)缺點(diǎn),不能判斷循環(huán)應(yīng)用的情況,所以就有了下面的方式
2)根搜索,從一些根對(duì)象(GCRoot)開(kāi)始遍歷搜索,如果一個(gè)對(duì)象無(wú)法被搜索到,說(shuō)明這個(gè)對(duì)象可以被回收了。
可以作為GCRoot的對(duì)象:
1 一些虛擬機(jī)棧中的對(duì)象;2 方法區(qū)中的類靜態(tài)屬性對(duì)象;3 方法區(qū)中的常量對(duì)象;4 Native棧中JNI的引用對(duì)象
收集階段主要有四種方式
1)標(biāo)記清除,最簡(jiǎn)單的算法,講標(biāo)記好的對(duì)象直接清除,速度快,但效率不高,內(nèi)存碎片
2)復(fù)制算法,每次使用可用內(nèi)存的一半,收集時(shí)將可用對(duì)象復(fù)制到另一半內(nèi)存,回收這一半
3)標(biāo)記整理,將存活對(duì)象整理到內(nèi)存區(qū)域的一端,剩余部分回收
4)分代回收,將內(nèi)存區(qū)域按對(duì)象存活周期劃分為青年代和老年代等,不同區(qū)域采用上面不同的收集算法。
Dalvik與ART
Android5.0 之前使用Dalvik虛擬機(jī),之后使用ART虛擬機(jī),下面是一些比較:
Dalvik在運(yùn)行時(shí)將字節(jié)碼轉(zhuǎn)換為機(jī)器碼,ART在安裝的時(shí)候就轉(zhuǎn)換為機(jī)器碼,這樣安裝好的應(yīng)用會(huì)占用更大的空間,但是運(yùn)行時(shí)少了轉(zhuǎn)換的時(shí)間,所以運(yùn)行更快
ART提供了更好的垃圾回收表現(xiàn),將垃圾回收時(shí),程序的暫停次數(shù)由兩次(分析、清理)減少到一次;程序暫停時(shí),并行的進(jìn)行垃圾回收處理;回收新近分配的、生命期短的對(duì)象,垃圾回收器花費(fèi)的時(shí)間更少
Android內(nèi)存分析工具
Memory Monitor
GC操作需要暫停其他線程,因此短時(shí)間頻繁的GC會(huì)對(duì)UI線程產(chǎn)生影響,導(dǎo)致頻繁GC一般有兩種情況,
大量的對(duì)象被創(chuàng)建又在短時(shí)間內(nèi)馬上被釋放,比如在View的onDraw方法中創(chuàng)建對(duì)象
Young Generation的內(nèi)存區(qū)域達(dá)到閥值,剩余空間不夠的時(shí)候,也會(huì)觸發(fā)頻繁GC
Android Studio提供了Memory Monitor來(lái)實(shí)時(shí)顯示應(yīng)用運(yùn)行時(shí)內(nèi)存占用情況,下邊藍(lán)色部分是現(xiàn)在占用的內(nèi)存,上面灰色的部分顯示是已回收的內(nèi)存。如果在圖上看到尖峰,也就是快速分配內(nèi)存又被回收,也就是發(fā)生了內(nèi)存抖動(dòng),這里就是需要優(yōu)化的地方。
Allocation Tracking
Allocation Tracking是DDMS中提供內(nèi)存工具,用來(lái)顯示一段時(shí)間內(nèi)的內(nèi)存分配情況。
選擇要跟蹤的進(jìn)程名,點(diǎn)擊Start Tracking開(kāi)始跟蹤,做一些操作后點(diǎn)擊Get Allocations就可以將這段操作中新分配的對(duì)象顯示出來(lái),點(diǎn)擊具體的對(duì)象可以在下面看到是哪一個(gè)方法分配的這個(gè)對(duì)象。
Heap Tool 與 MAT
Heap Tool可以查看當(dāng)前的內(nèi)存快照
從數(shù)據(jù)里可以看到當(dāng)前內(nèi)存的占用和回收情況,每次垃圾回收這里的數(shù)據(jù)都會(huì)更新,因?yàn)闀?huì)不斷獲取內(nèi)存數(shù)據(jù)刷新顯示,所以這時(shí)候?qū)?yīng)用操作會(huì)出現(xiàn)卡頓。
Heap Tool提供的是一個(gè)內(nèi)存的總體情況,圖表顯示的內(nèi)容比較簡(jiǎn)單,如果要具體分析的話最好生成.hprof文件,使用MAT工具進(jìn)行分析。
關(guān)于MAT工具的使用已經(jīng)有很多介紹,google官方曾經(jīng)寫過(guò)一個(gè)使用介紹,有興趣可以上網(wǎng)查閱。
一般用到MAT工具分析內(nèi)存都是因?yàn)榘l(fā)生了應(yīng)用發(fā)生了內(nèi)存泄漏,需要自己去分析可能泄漏的地方,然后用MAT工具去驗(yàn)證。而最近Square公司開(kāi)源了一個(gè)內(nèi)存泄漏檢測(cè)項(xiàng)目LeakCanary,極大地簡(jiǎn)化了這個(gè)過(guò)程,可以說(shuō)是Android內(nèi)存泄漏檢測(cè)的終極利器。
LeakCanary
A memory leak detection library for Android andjava.
LeakCanary會(huì)檢測(cè)應(yīng)用的內(nèi)存回收情況,如果發(fā)現(xiàn)有垃圾對(duì)象沒(méi)有被回收,就會(huì)去分析當(dāng)前的內(nèi)存快照,也就是上邊MAT用到的.hprof文件,找到對(duì)象的引用鏈,并顯示在頁(yè)面上。
使用:
在build.gradle文件中添加
在應(yīng)用的application onCreate方法中添加LeakCanary.install(this),如下
應(yīng)用運(yùn)行起來(lái)后,LeakCanary會(huì)自動(dòng)去分析當(dāng)前的內(nèi)存狀態(tài),如果檢測(cè)到泄漏會(huì)發(fā)送到通知欄,點(diǎn)擊通知欄就可以跳轉(zhuǎn)到具體的泄漏分析頁(yè)面。
Tips
就目前使用的結(jié)果來(lái)看,絕大部分泄漏是由于使用單例模式hold住了Activity的引用,比如傳入了context或者將Activity作為listener設(shè)置了進(jìn)去,所以在使用單例模式的時(shí)候要特別注意,還有在Activity生命周期結(jié)束的時(shí)候?qū)⒁恍┳远x監(jiān)聽(tīng)器的Activity引用置空。