Android記憶體洩漏定位與解決
問題現象
反覆點選被測試的Android App的toolbar介面,然後返回再點選。在此重複過程中,發現到一定次數時,頁面開啟速度變慢,有時達到5s,十分影響使用者體驗。該問題涉及app所採用的webview框架的所有介面,影響面大。
初步分析
載入介面慢,一般有2種情況:一是每次都慢,那麼與該介面的佈局(layout)效率或業務邏輯(主執行緒動畫/同步的業務邏輯)關係更大;另外一種是重複開啟幾次,會遇到一次變慢,並且迴圈發生,根據多次記憶體問題的分析經驗,會與記憶體洩漏關係更大。至於為何有如此的判斷依據,下文會進行解釋。
該問題屬於後者,因此首先從記憶體的角度進行分析。使用Android官方提供的DDMS工具,選中App所在程序,監控該程序的堆記憶體狀況,如下表所示,隨著重複開啟介面次數的增加,堆記憶體一直呈上升趨勢,而且,測試過程中,即使點選DDMS的Gause GC(Garbage Collector 記憶體垃圾回收)來主動觸發記憶體垃圾回收,堆記憶體也沒有下降。
從該圖可以判斷,無論是系統自發的GC或QA主動觸發,對記憶體都不會下降,說明有相當一部分物件被一直引用著,導致GC時不會去釋放這部分物件的記憶體,而每開啟一次介面,又會在堆上為新的物件分配記憶體,最終結果就是記憶體洩漏。
對記憶體洩漏有了初步判斷後,下一步是使用MAT工具分析該場景所有物件的記憶體佔用和引用關係,從而定位出具體導致洩漏的類。首先同樣地重複開啟該介面,在開啟第5次、10次、20次的時候,分別dump出當時的hprof檔案(相當於所有物件的記憶體畫像),3個檔案在MAT的sumary分析中,都指出DynamicBgDrawable這個類的物件存在記憶體洩漏的風險
於是問題的分析有了下一步的目標。繼續使用MAT檢視DynamicBgDrawable物件的引用鏈:
從上圖看出,GraphicContent這個類的mContents成員(WeakHashMap型別)是mBackground物件(DynamicBgDrawable型別)的root引用,這說明正是因為GraphicContent中mContents對mBackground的間接引用一直未釋放,才導致DynamicBgDrawable物件記憶體的洩漏,到這裡就可以從GraphicContent.java的程式碼繼續尋找問題的原因了。
程式碼&業務分析
從GraphicContent類的兩處程式碼可以看出,GraphicContent一直持有著View,而WeakHashMap這個結構,如果作為key的View沒有被釋放,作為value的GraphicContent也不會被釋放,而這裡的View,在實際執行時,傳的就是
真相大白
Android App是執行在Dalvik虛擬機器之上的Java程式,Dalvik是Java虛擬機器(JVM)針對Android改造的版本,許多機制沿襲了JVM的設計,包括記憶體管理。對JVM的記憶體管理和回收機制進行了解,能更深入地理解該Bug的分析手段和定位過程。
以上是Java虛擬機器(JVM)的記憶體區域劃分,Java只能在堆中存放物件,而不能在棧上分配物件,所有執行時產生的物件全部都存放於堆中,包括陣列。是一個執行緒的執行區域, 它儲存著一個執行緒中的方法的呼叫狀態,也可以說,一個Java執行緒的執行狀態,都由一個Java棧來儲存。每個執行緒都會有自己的Java棧, 不會相互訪問其他Java棧中的資料。同時,基本資料型別也是在棧中儲存,包括boolean、byte、char、short、int、float、long、double。所以在分析這個Bug時,只關心堆記憶體,而不關心棧記憶體。因為物件記憶體的洩漏(溢位)只會發生在堆記憶體上。
下面解釋開頭的問題:為什麼概率性載入緩慢,更有可能與記憶體相關。首先需要了解Dalvik虛擬機器的記憶體垃圾回收原理:
Dalvik中會維護一個物件的引用關係圖,如上圖所示,方塊代表一個物件,mark後的數字代表這個物件被持有的引用個數。當Dalvik進行GC時,首先會做“標記”,將每個物件被引用的次數進行標記。上圖中,Root是引用關係圖的起點,藍色方塊代表該物件被持有了引用,那麼它的記憶體不會被回收。白色方塊代表該物件沒有或即將不被持有引用。”標記”過程結束後,GC就進入”清除”過程,會把所有mark為0的物件記憶體釋放掉,從而完成一次GC的操作。
上圖就是GC進行“清除”操作前後的示意圖。可以看出,在回收前,連續的可用記憶體較少,等同於碎片較多,在回收後,連續的可用記憶體變多了。我們回到bug本身,由於每次開啟介面,都會為新的物件分配記憶體,於是上圖中的存活物件方塊會會越來越多,連續的未使用區域會越來越少,這時當下一次開啟介面時,因為碎片過多,無法分配記憶體給物件,特別是大物件,就會過早的引起GC。而物件越多,一次GC的時間會越長,從而加大了系統的負載,增加了App介面的調起時間。下一步,當完成GC後,如果有足夠的記憶體可分配,則是較好的情況,如果像該Bug的情況,佔用大片記憶體的物件一直被引用著而不被GC釋放,在下一次開啟介面時,Android系統就需要為這個App分配更大的堆記憶體,以保證記憶體分配成功。當記憶體洩漏到一定程度,系統無法保證為App分配足夠記憶體時,則記憶體溢位(Out Of Memory, OOM)就會發生。
上圖解釋了介面概率性開啟緩慢與記憶體更相關的原因。圖中黑色的步驟都會增加介面的載入時間,上面的黑色步驟,是由於記憶體碎片引起,會隨著開啟次數增多,發生概率加大。下面的黑色步驟,是根據GC後App所生堆記憶體大小相關,每次開啟介面的情況都會不一樣,因此,才會出現概率性的開啟緩慢,從而為我們分析這種問題提供思路——就是開頭提到的,如果是概率性開啟緩慢,可以優先考慮和記憶體問題相關。
解決方案
將mView改為弱引用,每次垃圾回收(GC)時,都會回收View物件,從而使WeakHashMap中的value(GraphicContent)物件也能自動得到釋放。
總結
該Bug的分析和解決過程,具有典型的代表性,在實際專案中,有多個Android記憶體洩漏的Bug,都是採用上述的定位方法和分析工具進行解決的,是一套通用且有效的Bug定位方案。而相互引用的問題,等價於死鎖情況,也是程式中典型的問題場景。弱引用的使用有效地解決業務和記憶體洩漏的問題,在Android app的記憶體洩漏和溢位的解決中,經常會被採用,也可以作為Code Review的一個關注點進行推廣。
更多幹貨分享請關注”百度MTC學院“http://mtc.baidu.com/academy/article相關推薦
Android記憶體洩漏定位與解決
問題現象 反覆點選被測試的Android App的toolbar介面,然後返回再點選。在此重複過程中,發現到一定次數時,頁面開啟速度變慢,有時達到5s,十分影響使用者體驗。該問題涉及app所採用的webview框架的所有介面,影響面大。 初步分析 載入介面慢,一
基於Android Studio的記憶體洩漏檢測與解決全攻略
自從Google在2013年釋出了Android Studio後,Android Studio憑藉著自己良好的記憶體優化,酷炫的UI主題,強大的自動補全提示以及Gradle的編譯支援正逐步取代Eclipse,成為主流的Android開發IDE。Android Studio在
Handler記憶體洩漏分析與解決方法
最近整理完Android中訊息機制的知識後,想到Handler記憶體洩漏相關的問題也可以順便整理一下,便有了這篇文章,也方便以後自己查閱 為什麼Handler會造成記憶體洩漏 下面是一段簡單的Handler使用 public class MainActivity extend
Android記憶體洩漏查詢和解決adb shell dumpsys meminfo packagement
1.通過adb shell dumpsys meminfo packageName來檢視記憶體使用狀況 在沒有開啟應用的情況下,該命令返回的資料是這樣的: 2.開啟這個應用的MainActivity,再通過命令檢視: 可以看到打印出來很多的資訊,而對於我們檢
Android記憶體洩漏場景及解決方法
本文包括以下內容: 1. 記憶體洩漏原理 2. Android記憶體洩漏發生的情況 3. 檢測記憶體洩漏的工具、方法 4. 如何避免記憶體洩漏 更多Android面試相關請點選 - 四步準備Android面試 - Android開發概要 - 大疆提前批第一次電面
Android記憶體洩漏檢測與MAT使用
公司相關專案需要進行記憶體優化,所以整理了一些分析記憶體洩漏的知識以及工作分析過程。 本文中不會刻意的編寫一個記憶體洩漏的程式,然後利用工具去分析它。而是通過介紹相關概念,來分析如何尋找記憶體洩漏,並附上自己的專案實戰過程。 撰寫過程中,本人深感JVM、作業
Android記憶體洩漏與記憶體溢位
Android記憶體洩漏與記憶體溢位 記憶體洩漏 什麼是記憶體洩漏 記憶體洩漏的原因 記憶體洩漏檢測工具LeakCanary Java中的記憶體分配 Java中的四種引用型別 騰訊記憶體洩漏分析
Android記憶體洩漏問題分析及解決方案
大家新年好,由於工作繁忙原因,有好一段時間沒有更新博文了(當然Github是一直都有更新的),趁著年底有點放假時間,我覺得抽空更新下部落格,總結一下工作中最常見記憶體洩漏問題,也是自己之前踩過的坑,為了讓大家少走彎路,系統全面總結一下記憶體洩漏問題分析原因及尋找解決方案。 概念 首
記憶體優化 . 記憶體洩露 記憶體溢位 記憶體抖動 分析與解決.android stido 工具
記憶體分配 dalvik 5.0之前 art 5.0之後用 根據執行的特定的資料型別 不同分配記憶體 卡頓 是怎麼形成的 卡頓的解決方式 ANR 講下 GC 回收導致 畫面卡頓的問題: 比如自定義view 中 繪製第一個畫面 ,繪製完
記憶體不足引起的SIGKILL:一個緩衝區不斷增長問題的定位與解決
新版本的錄製程式終於快完工了,在添加了一個新特性“報警錄製”後,就開始測試了。 一開始就不順利:程式正常執行一段時間後就會崩潰,由於程式添加了守護程序,在崩潰後會自動重啟。 因此測試得到的結果就是:程式執行一段時間後就自動重啟,並不斷持續;
如何快速定位Android記憶體洩漏位置
如果所有的物件都可以被順利回收就沒有本文的誕生了,舉個簡單的例子,我們在開發中經常使用單例模式,單例的靜態特性導致其生命週期同應用一樣長。有時建立單例時如果我們需要Context物件,如果傳入的是Application的Context那麼不會有問題。如果傳入的是Activity的Context物件,那麼當
Android記憶體洩漏解決方案(OOM)
為什麼會有記憶體洩漏? 一個不會被使用的物件,因為另一個正在使用的物件持有該物件的引用,導致它不能正常被回收,而停留在堆記憶體中,記憶體洩漏就產生了 Android系統為每個應用分配的記憶體是有限的,記憶體洩漏會使我們的應用記憶體隨著時間不斷的增加,造成應用
Android記憶體洩漏8種可能及完善解決方案
借鑑自https://www.jianshu.com/p/ac00e370f83d 一般記憶體洩漏(traditional memory leak)的原因是:由忘記釋放分配的記憶體導致的。(Cursor忘記關閉等) 邏輯記憶體洩漏(logical memory leak)
Android記憶體洩漏(執行緒造成的記憶體洩漏與資源未關閉造成的記憶體洩漏)
一.執行緒造成的記憶體洩漏 對於執行緒造成的記憶體洩漏,也是平時比較常見的,leakCanary官方Demo就是執行緒成造成的記憶體洩漏,使用了AsyncTask去執行非同步執行緒,現在我們換個
有關Android Handler記憶體洩漏分析及解決辦法
1、Android的開發工具是java,這能幫助我們解決很底層的問題 包括:記憶體管理,平臺依賴。然而,有時候專案依然會報OOM錯誤,so垃圾收集器在哪? 2、我主要研究一種情況:記憶體中較大物件很長一段時間內不能被釋放。這方面並不完全算作記憶體溢位,物件會在某一時間點上被
利用 LeakCanary 來檢查 Android 記憶體洩漏 6.0以上版本空指標解決
1.3. 在6.0預覽版報錯 * FAILURE: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a nul
Android 5.1 WebView記憶體洩漏問題及解決
問題背景 在排查專案記憶體洩漏過程中發現了一些由WebView引起的記憶體洩漏,經過測試發現該部分洩漏只會出現在android 5.1及以上的機型。雖然專案使用WebView的場景並不多,但秉承著一個洩漏都不放過的精神,我們肯定要把它給解決了。 遇到的問題
linux系統下too many files open如何定位與解決
title 存儲 適應 print park lin 理論 描述符 通過 當應用進程打開的文件句柄數大於系統設置的句柄數,服務就會報錯too many files open,那麽如何解決這個問題呢? 1:查看當前系統設置的最大句柄數 命令:ulimit -a;可以看見
[Android]Android記憶體洩漏你所要知道的一切(翻譯)
以下內容為原創,歡迎轉載,轉載請註明 來自天天部落格:http://www.cnblogs.com/tiantianbyconan/p/7235616.html Android記憶體洩漏你所要知道的一切 原文:https://blog.aritraroy.in/everything-
HTML5離線應用無法更新的定位與解決
一、些許前提 最近在製作一個Web應用, 其中用到了HTML5的離線應用功能(offline application), 離線應用的概念就不再闡述, 可以檢視這兩篇文章: http://www.ibm.com/developerworks/cn/web/1011_gu