編輯:關於Android編程
最近負責的Gallery模塊測試時頻繁報出使用一段時間後就發生Crash,通過Log查看發現是OOM引發,本篇文檔記錄解決該問題的分析過程。
該OOM問題測試沒有找到復現步驟,只是報出使用一段時間就會發生OOM這個非常寬泛的現象,沒有復現步驟,沒有確定時間節點。查看Log每次發生OOM的代碼調用棧也不盡相同。貼出出現OOM的幾段Log。
vc+087XExNq05r/VvOTKsaOsz7XNs21lbW9yeb/VvOSyu7m7wcujrLW81sK3osn6wctPT02jrNXiwO+2vMrH1Nq21M28xqy9+NDQstnX96Os0vK0y8nqx+vBy73PtPO1xMTatOa/1bzkoaM8YnIgLz4NCtbBtMvT0NK7uPa087jFtcSy6dXSt73P8qO6zsrM4rP2z9a1xMqxu/rKx9TaR2FsbGVyebS0vai087XEQml0bWFwttTP86Oswcu94rn9R2FsbGVyedK1zvHB97PMutzI3dLXvs274buz0snV4rj2uf2zzLbgsOu3osn61NpQaG90b1BhZ2W958Pmo6zSsr7Nysfkr8DAtPPNvL3nw+ajrLWrye7I68HLveK3os/WR2FsbGVyedLRvq221NXiv+m9+NDQwcu0psDto6yyorK7yse0882809C24LTzo6y+zdK7tM7Q1LS0vai24LTztcRCaXRtYXC21M/zo6y2+MrHvavSu7j2tPPNvLfWs8m24Lj2x/jT8rzT1Ni9+MC0o6y+38zlv8nS1LLpv7RHYWxsZXJ5tPrC69bQTG9jYWxJbWFnZcDg1tC1xHJlcXVlc3RMYXJnZUltYWdlt723qKOsy/zA+9PDtcTKx0JpdG1hcFJlZ2lvbkRlY29kZXK8vMfJo6y31r/ptuC0zrzT1Ni08828o6zV4sDvsrvXuMr2oaPU2sXFsulMb2e3os/WzsrM4reiyfrU2nJlZm9jdXOy2df3vLjCyrTzo6yy4srU0rLTodakwcu4w7XjoaPV4sDvcmVmb2N1c8rHTVRLxr3MqNXrttRHYWxsZXJ51/a1xNK7uPbQwrmmxNyjrMv8xNy21L6wye7NvMasvfjQ0NbYvtu9uS+199X7zbzGrLniyKbWtaGjxcWy6dbYteO3xdTacmVmb2N1c7LZ1/fJz6GjPC9wPg0KPGgxIGlkPQ=="復現問題">復現問題
上述Log分析都只是邏輯上的分析,沒有驗證,針對這些分析需要工具幫忙確認進一步縮小排查范圍。這裡使用DDMS 工具分析,打開DDMS,選擇Gallery所在的進程,點擊Update Heap選項。切換到Heap視圖邊操作邊查看內存變化.
留意占用空間最多的項,這裡是byte array,通過在AlbumSetPage/AlbumPage來回切換滑動觀察到該項值大概穩定在30M~50M之前,並且data object/class object值也沒有出現一直不斷的增加的現象,點擊Cause GC觸發垃圾回收器工作,上述這些值也有明顯的回落。
在加入浏覽大圖的PhotoPage界面,來回在PhotoPage滾動浏覽圖片,觀察到內存大概穩定在50~70M之間。
當返回AlbumPage界面,點擊Cause GC觸發垃圾回收器工作,內存回落到之前的30M~50M區間。
現在在加入refocus操作.發現進入refocus操作界面內存上升,退出後內存沒有明顯下降,曙光出現了。於是不停的進入退出refocus界面,大約來回二十多次後,問題復現了,logcat發現報錯也是OOM,到此找到了復現問題的步驟。
有了上面的復現步驟,開始用MAT工具定性分析內存使用情況。
利用命令:
adb shell am dump heap [package name] >/data/local/tmp/gallery6.hprof
生成hprof文件。
再用命令:hprof-conv gallery6.hprof gallery6_.hprof轉換成MAT工具可以識別的格式。
最終生成的hprof文件如下:
點擊Dominator Tree:
可以看到這裡有多個refocusView和RefocusActivity存在多個實例,這個很可能是突破口,
先排查RefocusView單擊鼠標右鍵,選擇List Objects–>with incoming references
繼續選中過濾出來的RefocusView,鼠標右鍵,選擇
Path to GC Roots–>exclude weak/soft references.
可以看到RefocusView—->ApertureSlider—->ValueAnimator的一條引用鏈。
同樣的方式在排查RefocusActivity發現跟RefocusView相關。
再看同樣可疑的Bitmap對象還是指向RefocusView.
看到這,問題基本已經有結論了:
RefocusActivity—>RefocusView—->ApertureSlider—->ValueAnimator這條引用關系鏈存在問題。
為了快速驗證分析結論是否正確,在代碼中將ApertureSlider去除。
再次檢驗結果如下:
可以看到沒有再出現多個RefocusView/RefocusActivity/Bitmap實例了。
復測也沒有在出現OOM。
代碼分析
通過上面的一步步分析,我們已經找到了問題原因出在RefocusView上,那麼它為什麼沒有GC回收呢,再次梳理下邏輯思路:
RefocusActivity--->RefocusActivity的onCreate裡加載RefocusView
---->RefocusView裡持有ApertureSlider--->ApertureSlider持有ValueAnimator--->ValueAnimator沒有被回收
現在進入ApertureSlider查看ValueAnimator,果然一進入就發現了ValueAnimator定義存在問題.
這裡聲明了一個static 類型的ValueAnimator,並且在構造方法裡對它設置了動畫監聽器。而正是這個靜態類型對ApertureSlider保持了一個引用,按照上面梳理的思路,在反向推回去,最終導致了RefocusActivity無法被GC回收。找到了問題點,就好修改了,去掉 static final修飾符即可。從一個小小的變量引發內存洩漏,導致最後出現整個應用的Crash。開篇Log裡提示的錯誤,算是正好撞在了槍口上,一次申請了較大的內存空間,將問題放大凸顯了出來。
總結回顧
分析問題的思路
回過頭來看,本篇的問題實在顯而易見。但透過它還是能收獲一些知識。
大膽假設:一個APP做到後期,往往代碼量很大,成千上萬的類文件,錯綜復雜的邏輯鏈條穿插其中。除非顯而易見的問題,否則最好不要一頭扎進細節處開始查詢問題,先在大腦中過一遍發生問題的場景是什麼?順著腦海中的思路假設問題發生的條件。 小心求證:有了猜測,就需要嚴謹的分析驗證。在這個問題中,一開始單從Log中我猜測的是創建Bitmap對象太大了,代碼中直接按大圖進行decode,也確實在代碼中找到了生成圖片時設置的采樣值是1(inSimpleSize=1),這樣如果面對的是一張高清大圖,非常大的可能出現OOM。但驗證發現調整圖片采樣值只是降低了OOM發生的概率,沒有從根本上根除問題。於是繼續猜測分析找到了問題的根本原因。
MAT中的一些概念
outgoing references:被當前實例引用到的所有對象。通過它可以看出當前實例”抓著”哪些對象不放。 incoming references:與outgoing references相反,表示引用到當前實例的所有對象。通過它可以看出誰在內存中”抓著”當前實例不放。 Shallow size:對象本身占用內存的大小,不包含其引用的對象。 Retained Heap:被一個對象所引用到的所有對象的Shallow size總和。
Android draw9patch 圖片制作與使用理解一下4句話: 上邊 決定左右拉升不變形 左邊 決定上下拉升不變形 右邊 設置內容高度區域 下邊 設置內容寬
在做項目的時候,想在 ExpandableListView 中嵌套一個 GridView,在實現的過程中,遇到了不少坑,所以寫篇博客記錄一下,也順便幫助下和我一樣的新手。
在同組項目進行共享時,容易把本地的配置文件比如*.iml等文件上傳至共享服務器,這樣會對隊友造成巨大的麻煩,為了解決這個問題,可以使用下面方法解決,下面以上傳到服務器的a
預備知識 android手機的內部存儲設備分RAM和ROM,RAM是運行內存,掉電就會失去所有內容;ROM中的內容掉電後也不會丟失。 比如一台手機的規格