本文轉自內存優化方向探討html
最近複習對以往項目中和學習中一些內存優化的方法進行下探討和彙總. 雖然如今Android設備的內存愈來愈大,4GB內存手機已經至關普片了,可是隻有有效的控制好內存才能將你的應用性能發揮到極致.因此控制好內存如今依然是Android應用中一個重要的技術指標.android
開篇以前咱們先說說內存.內存已是咱們很熟悉的概念了,可是你能說出手機內存和電腦內存的區別嗎?手機內存之間有差別嗎?內存是否大就好?git
從本質上看二者是相同的,電腦上用的是DDR內存,而移動設備上的是LPDDR內存.LPDDR的英文全稱爲Low Power Double Data Rate,LP即爲低功耗,專門用於移動式的電子產品。相較於PC領域的DDR4內存,LPDDR4形態的內存更省電.程序員
如今主流的運行內存分別有LPDDR三、LPDDR4以及LPDDR4X.github
從上圖能夠看出LPDDR4的性能要比LPDDR3高出一 倍,而LPDDR4X相比LPDDR4工做電壓更低,因此也比LPDDR4省電20%-40%。固然圖中的數據是標準數據,不一樣的生成 廠商會有一些低頻或者高頻的版本,性能方面高頻要好於低頻.其實從上面一點咱們就能得出結論. 若是一個用8GB LPDDR4X的手機性能通常是優於 12GB LPDDR3手機的. 可是內存並非一個孤立的概念,它跟操做系統、應用生態這些因素都有關. 相信你們也對幾年前的IPhone6 1GB的運行內存就能吊打4-6GB的Android手機還印象深入.一樣的內存,AndroidQ系統性能就會比Android4.0好.封閉規範的IOS系統就會比開放的Android系統好.web
通常你們對手機內存都有一個比較粗略的概念,就是分堆和棧,但實際上仍是複雜得多. 算法
主要包括程序計數器PC、虛擬機棧、本地方法棧、Java堆、方法區、常量池等,這裏推薦你們能夠看下 詳解內存優化的前因後果一文,有詳細的介紹.這裏我只說下兩個容易陷入的誤區數組
我曾經遇到一個問題,在加載一個5MB的圖片到界面時忘記壓縮出現了OOM,但當我查看日誌時發現可用堆內存明明還大於5MB,爲何就OOM呢? 緩存
這也是很多人有的誤區之一,甚至有人當內存是洪水猛獸,用得越少越好,這樣容易優化過分產生其餘問題.如今手機的發展內存愈來愈多,若是隻有一個具體的數值,作硬指標實際上是沒有必要的.APP是否佔用過多的內存,跟設備、性能、和當時的狀況也有關.可讓高端設備使用更多的內存,作到針對設備性能的好壞使用不一樣的內存分配和回收策略.bash
要作到針對設備進行優化處理,須要一個良好的架構支撐,架構須要支持如下幾點:
設備分級: 使用相似device-year-class的策略對設備分級,對於低端機用戶能夠關閉複雜的動畫,或者是某些功能;使用 565格式的圖片,使用更小的緩存內存等。在現實環境下,不是每一個用戶的設備都跟咱們的測試機同樣高端,在開發過程我 們要學會思考功能要不要對低端機開啓、在系統資源吃緊的時候能不能作降級
緩存管理:咱們須要有一套統一的緩存管理機制,能夠適當地使用內存;當"系統有難"時,也要責無旁貸地歸還.咱們能夠 使用OnTrimMemory回調,根據不一樣的狀態決定釋放多少內存.對於大項目來講,可能存在幾十上百個模塊,統一緩存管 理能夠更好地監控每一個模塊的緩存大小.
進程模型:一個空的進程也會佔用10MB的內存,而有些應用啓動就有十幾個進程,甚至有些應用已經從雙進程保活升級到 四進程保活,因此減小應用啓動的進程數、減小常駐進程、節制的保活,對低端機內存優化很是重要.
安裝包大小: 安裝包中的代碼、資源、圖片以及so庫的體積,跟它們佔用的內存有很大的關係.一個80MB的應用很難在 512MB內存的手機上流暢運行.這種狀況咱們須要考慮針對低端機用戶推出輕量版本,例如QQ音樂HD、快手極速版、今日頭條極速版都是這個思路.
圖片資源內存通常佔APP總內存很大一部分,因此作內存優化永遠沒法避開圖片內存這個"永恆主題". 即便把全部的Bitmap都放到Native內存,並不表明圖片內存問題就徹底解決了,這樣作只是提高了系統內存利用率,減小了 GC帶來的一些問題而已. 那咱們該如何優化圖片內存呢?我推薦如下兩種方法:
圖片內存優化的前提是收攏圖片的調用,這樣咱們能夠作總體的控制策略.例如低端機使用565格式、更加嚴格的縮放算法, 可使用Glide、Fresco或者採起自研均可以.並且須要進一步將全部Bitmap.createBitmap、BitmapFactory相關的接口也一併收攏.
在統一圖片庫後就很是容易監控Bitmap的使用狀況了,這裏主要有兩點須要注意.
講完設備分級和Bitmap優化,咱們發現架構和監控須要兩手抓,一個好的架構能夠減小甚至避免咱們犯錯,而一個好的監控 能夠幫助咱們及時發現問題.
內存泄漏簡單理解就是沒有及時的回收不用的內存,排查和解決內存泄漏也是內存優化沒法避開的工做之一.
內存泄漏主要分兩種狀況,一種是同一個對象泄漏,還有一種狀況更加糟糕,就是每次都會泄漏新的對象,可能會出現幾百上 千個無用的對象.
不少內存泄漏都是框架設計不合理所致使,各類各樣的單例滿天⻜,MVC中Controller的生命週期遠遠大於View.優秀的框架 設計能夠減小甚至避免程序員犯錯,固然這不是一件容易的事情,因此咱們還須要對內存泄漏創建持續的監控.
1. Java內存泄漏: 創建相似LeakCanary自動化檢測方案,至少作到Activity和Fragment的泄漏檢測.在開發過程,咱們但願 出現泄漏時能夠彈出對話框,讓開發者更加容易去發現和解決問題.內存泄漏監控放到線上並不容易,咱們能夠對生成的 Hprof內存快照文件作一些優化,裁剪大部分圖片對應的byte數組減小文件大小。好比一個100MB的文件裁剪後通常只剩下 30MB左右,使用7zip壓縮最後小於10MB,增長了文件上傳的成功率
2. OOM監控: 美團有一個Android內存泄露自動化鏈路分析組件Probe,它在發生OOM的時候生成Hprof內存快照,而後經過 單獨進程對這個文件作進一步的分析.不過在線上使用這個工具⻛險仍是比較大,在崩潰的時候生成內存快照有可能會導 致二次崩潰,並且部分手機生成Hprof快照可能會耗時幾分鐘,這對用戶形成的體驗影響會比較大.另外,部分OOM是由於 虛擬內存不足致使,這塊須要具體問題具體分析.
3.Native內存泄漏監控: 在 WeMobileDev的一篇文章《微信Android終端內存優化實踐》中,微信也作了一些監控方案上面的嘗試.
4. 針對沒法重編so的狀況: 使用了PLT Hook攔截庫的內存分配函數,其中PLT Hook是Native Hook的一種方案.而後重定向到咱們本身的實現後記錄分配的內存地址、大小、來源so庫路徑等信息,按期掃描分配與釋放是否配 對,對於不配對的分配輸出咱們記錄的信息。
5. 針對可重編的so狀況: 經過GCC的"-finstrument-functions"參數給全部函數插樁,樁中模擬調用棧入棧出棧操做;經過ld 的「–wrap」參數攔截內存分配和釋放函數,重定向到咱們本身的實現後記錄分配的內存地址、大小、來源so以及插樁記錄的 調用棧此刻的內容,按期掃描分配與釋放是否配對,對於不配對的分配輸出咱們記錄的信息.
在實驗室或者內部試用環境,咱們也能夠經過Debug.startAllocCounting來監控Java內存分配和GC的狀況,須要注意的是這個 選項對性能有必定的影響,雖然目前還可使用,但已經被Android標記爲deprecated. 經過監控,咱們能夠拿到內存分配的次數和大小,以及GC發起次數等信息.
long allocCount = Debug.getGlobalAllocCount();
long allocSize = Debug.getGlobalAllocSize();
long gcCount = Debug.getGlobalGcInvocationCount();
複製代碼
上面的這些信息彷佛不太容易定位問題,在Android 6.0以後系統能夠拿到更加精準的GC信息.
//運行的GC次數
Debug.getRuntimeStat("art.gc.gc-count");
//GC使用的總耗時,單位是毫秒
Debug.getRuntimeStat("art.gc.gc-time");
//阻塞式GC的次數
Debug.getRuntimeStat("art.gc.blocking-gc-count");
//阻塞式GC的總耗時
Debug.getRuntimeStat("art.gc.blocking-gc-time");
複製代碼
須要特別注意阻塞式GC的次數和耗時,由於它會暫停應用線程,可能致使應用發生卡頓。咱們也能夠更加細粒度地分應用場景統計,如登錄、啓動、主業務等關鍵場景.
常見的優化方式 如handler、webview、內存抖動、匿名內存類等等網上還有不少就不一一介紹了.建議參考Android內存優化全解析、探索 Android 內存優化方法
在進行內存的優化前,咱們首先搞清楚要優化的程度?內存產生了什麼異常和卡頓? 只有在明確了APP現狀和優化目標後,咱們才能去進行下一步的操做.在探討了內存優化的思路時,針對不一樣的設備、設備不一樣的狀況,咱們但願能夠給用戶不一樣的體驗.這裏我主要講到了關於 Bitmap內存優化和內存泄漏排查、監控的一些方法.但願對你們有所幫助.