Google近期在Udacity上發佈了Android性能優化的在線課程,分別從渲染,運算與內存,電量幾個方面介紹瞭如何去優化性能,這些課程是Google以前在Youtube上發佈的Android性能優化典範專題課程的細化與補充。html
下面是內存篇章的學習筆記,部份內容與前面的性能優化典範有重合,歡迎你們一塊兒學習交流!java
衆所周知,與C/C++須要經過手動編碼來申請以及釋放內存有所不一樣,Java擁有GC的機制。Android系統裏面有一個Generational Heap Memory的模型,系統會根據內存中不一樣的內存數據類型分別執行不一樣的GC操做。例如,最近剛分配的對象會放在Young Generation區域,這個區域的對象一般都是會快速被建立而且很快被銷燬回收的,同時這個區域的GC操做速度也是比Old Generation區域的GC操做速度更快的。android
除了速度差別以外,執行GC操做的時候,全部線程的任何操做都會須要暫停,等待GC操做完成以後,其餘操做纔可以繼續運行。性能優化
一般來講,單個的GC並不會佔用太多時間,可是大量不停的GC操做則會顯著佔用幀間隔時間(16ms)。若是在幀間隔時間裏面作了過多的GC操做,那麼天然其餘相似計算,渲染等操做的可用時間就變得少了。app
Android Studio中的Memory Monitor能夠很好的幫助咱們查看程序的內存使用狀況。工具
內存泄漏表示的是再也不用到的對象由於被錯誤引用而沒法進行回收。性能
發生內存泄漏會致使Memory Generation中的剩餘可用Heap Size愈來愈小,這樣會致使頻繁觸發GC,更進一步引發性能問題。學習
舉例內存泄漏,下面init()方法來自某個自定義View:優化
private void init() { ListenerCollector collector = new ListenerCollector(); collector.setListener(this, mListener); }
上面的例子容易存在內存泄漏,若是activity由於設備翻轉而從新建立,自定義的View會自動從新把新建立出來的mListener給綁定到ListenerCollector中,可是當activity被銷燬的時候,mListener卻沒法被回收了。動畫
下圖演示了Android Tools裏面的Heap Viewer的功能,咱們能夠看到當前進程中的Heap Size的狀況,分別有哪些類型的數據,佔比是多少。
Memory Churn內存抖動,內存抖動是由於在短期內大量的對象被建立又立刻被釋放。瞬間產生大量的對象會嚴重佔用Young Generation的內存區域,當達到閥值,剩餘空間不夠的時候,會觸發GC從而致使剛產生的對象又很快被回收。即便每次分配的對象佔用了不多的內存,可是他們疊加在一塊兒會增長Heap的壓力,從而觸發更多其餘類型的GC。這個操做有可能會影響到幀率,並使得用戶感知到性能問題。
解決上面的問題有簡潔直觀方法,若是你在Memory Monitor裏面查看到短期發生了屢次內存的漲跌,這意味着頗有可能發生了內存抖動。
同時咱們還能夠經過Allocation Tracker來查看在短期內,同一個棧中不斷進出的相同對象。這是內存抖動的典型信號之一。
當你大體定位問題以後,接下去的問題修復也就顯得相對直接簡單了。例如,你須要避免在for循環裏面分配對象佔用內存,須要嘗試把對象的建立移到循環體以外,自定義View中的onDraw方法也須要引發注意,每次屏幕發生繪製以及動畫執行過程當中,onDraw方法都會被調用到,避免在onDraw方法裏面執行復雜的操做,避免建立對象。對於那些沒法避免須要建立對象的狀況,咱們能夠考慮對象池模型,經過對象池來解決頻繁建立與銷燬的問題,可是這裏須要注意結束使用以後,須要手動釋放對象池中的對象。
關於Allocation Tracker工具的使用,不展開了,參考下面的連接:
下面演示一個例子,如何經過修改代碼來避免內存抖動。優化以前的內存檢測圖:
定位代碼以後,修復了String拼接的問題:
優化以後的內存監測圖:
上面提到了三種測量內存的工具,下面再簡要歸納一下他們各自的特色: