本文的着重點爲第一點,總結概述下降應用運行內存的技巧。在這裏咱們再也不細述PSS、USS等概念與Android應用的內存管理,如對這部份內容感興趣,可自行閱讀文末的參考文章。php
內存泄露:簡單來講對象因爲編碼錯誤或系統緣由,仍然存在着對其直接或間接的引用,致使系統沒法進行回收。內存泄露,容易留下邏輯隱患,同時增長了應用內存峯值與發生OOM的機率。它屬於bug issue,是咱們必定要修改的。html
下面是形成內存泄露的一些常見緣由,可是如何創建一套發現內存泄露、解決內存泄露的閉環方案,纔是咱們工做的重點。java
一. 內存泄露的監控方案android
Square的開源庫leakcanry是一個很是不錯的選擇,它經過弱引用方式偵查Activity或對象的生命週期,若發現內存泄露自動dump Hprof文件,經過HAHA庫獲得泄露的最短路徑,最後經過notification展現。git
內存泄露判斷與處理的流程以下圖 ,各自運行的進程空間(主進程經過idlehandler,HAHA分析使用的是單獨的進程):github
微信在leakcanry推出以前已經有了本身的內存泄露監控體系,與leakcanry大體有如下的區別:web
事實上,經過對leakcanry作簡單的定製,咱們就能夠實現如下一個內存泄露監控閉環。 算法
二. 對系統內存泄露的Hack Fix緩存
AndroidExcludedRefs列出了一些因爲系統緣由致使引用沒法釋放的例子,同時對於大多數的例子,都會提供建議如何經過hack的建議去修復。在微信中,對TextLine、InputMethodManager、AudioManger、android.os.Message也採用了相似Hack的方式(詳細可看參考資料)。性能優化
三. 經過兜底回收內存
Activity泄漏會致使該Activity引用到的Bitmap、DrawingCache等沒法釋放,對內存形成大的壓力,兜底回收是指對於已泄漏Activity,嘗試回收其持有的資源,泄漏的僅僅是一個Activity空殼,從而下降對內存的壓力。
作法也很是簡單,在Activity onDestory時候從view的rootview開始,遞歸釋放全部子view涉及的圖片,背景,DrawingCache,監聽器等等資源,讓Activity成爲一個不佔資源的空殼,泄露了也不會致使圖片資源被持有。
1
2
3
4
5
6
7
8
9
|
…
…
Drawable d = iv.getDrawable();
if (d != null) {
d.setCallback(null);
}
iv.setImageDrawable(null);
...
...
|
總的來講,咱們不是隻懂得一些內存泄露解決方法就能夠,更重要的是經過平常測試與監控,獲得內存泄露檢測與修改的一整套閉環體系。
當咱們能確保應用中不會出現內存泄露時,咱們須要一些其餘的方法來下降運行時的內存。更多的時候,咱們其實只但願下降應用發生OOM的機率。
Android OOM:
一. 減小bitmap佔用的內存
說到內存,bitmap必然是這裏的大頭。對於bitmap內存佔用,想說的有如下幾點:
一個好的imageLoader,能夠將2.X、4.X或5.X對圖片加載的處理對使用者隱藏,同時也能夠將自適應大小、質量等放於框架中。
二. 自身內存佔用監控
對於系統函數onLowMemory等函數是針對整個系統而已的,對於本進程來講,其dalvik內存距離OOM的差值並無體現,也沒有回調函數供咱們及時釋放內存。倘若能有那麼一套機制,能夠實時監控進程的堆內存使用率,達到設定值即關於通知相關模塊進行內存釋放,這會大大的下降OOM。
1
2
|
Runtime.getRuntime().maxMemory();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
1
|
WindowManagerGlobal.getInstance().startTrimMemory(TRIM_MEMORY_COMPLETE);
|
三. 使用多進程
對於webview,圖庫等,因爲存在內存系統泄露或者佔用內存過多的問題,咱們能夠採用單獨的進程。微信當前也會把它們放在單獨的tools進程中
四. 上報OOM詳細信息
當系統發生OOM的crash時,咱們應當上傳更加詳細的內存相關信息,方便咱們定位當時內存的具體狀況。
其餘例如使用large heap、inBitmap、SparseArray、Protobuf等再也不一一細述,對代碼採用優化--埋坑--優化--埋坑
的方式並不推薦。咱們應該着力於創建一套合理的框架與監控體系,能及時的發現諸如bitmap過大、像素浪費、內存佔用過大、應用OOM等問題。
Java擁有GC的機制,不一樣的系統版本GC的實現可能有比較大的差別。可是不管哪一種版本,大量的GC操做則會顯著佔用幀間隔時間(16ms)。若是在幀間隔時間裏面作了過多的GC操做,那麼天然其餘相似計算,渲染等操做的可用時間就變得少了。
一. GC的類型
GC的類型有如下幾種,其中GC_FOR_ALLOC是同步方式進行,對應用幀率的影響最大。
二. 內存抖動現象
Memory Churn內存抖動,內存抖動是由於在短期內大量的對象被建立又立刻被釋放。瞬間產生大量的對象會嚴重佔用內存區域,當達到閥值,剩餘空間不夠的時候,會觸發GC從而致使剛產生的對象又很快被回收。即便每次分配的對象佔用了不多的內存,可是他們疊加在一塊兒會增長Heap的壓力,從而觸發更多其餘類型的GC。這個操做有可能會影響到幀率,並使得用戶感知到性能問題。
經過Memory Monitor,咱們能夠跟蹤整個app的內存變化狀況。若短期發生了屢次內存的漲跌,這意味着頗有可能發生了內存抖動。
三. GC優化
經過Heap Viewer,咱們能夠查看當前內存快照,便於對比分析哪些對象有可能發生了泄漏。更重要的工具是Allocation Tracker,追蹤內存對象的類型、堆棧、大小等。手Q有作一個統計工具,對Allocation Tracker的原始數據,按照(類型&堆棧)的組合(堆棧取棧頂的5層)統計某一種對象分配的大小、次數。同時按照次數、大小的排序,從多/大到少/小結合代碼分析,並自頂向下的逐輪進行優化。
這樣,咱們就能夠快速知道發生內存抖動時,是由於哪些變量的建立形成頻繁GC。通常來講咱們須要注意如下幾個方面:
1
2
3
4
5
|
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
|
咱們並不能將內存優化中用到的全部技巧都一一說明,並且隨着Android版本的更替,可能不少方法都會變的過期。我在想更重要的是咱們能持續的發現問題,精細化的監控,而不是一直處於」哪一個有坑填哪裏的」的窘況。在這裏給你們的建議有:
當前微信內存監控體系中也存在一些不盡人意的地方,在將來的日子裏也一樣須要努力去優化。