Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 使用Android Studio分析內存問題

使用Android Studio分析內存問題

編輯:關於Android編程

大家好!本人是即將畢業學生一枚,閒暇時間經常看大神們寫的博客學到很多東西。最近在做畢業設計的時候遇到一些問題,然後把自己的問題和解決方法總結一下,有不對的地方希望大家多多包涵,提出批評與指導。

這篇博文主要介紹使用AndroidStudio對內存進行分析和跟蹤,還有就是從源碼角度解決ImageLoader引起的OOM問題。

我正在做的項目使用到了ImageLoader來加載圖片,我也是第一次使用,就拿來直接用了。寫完的代碼運行很正常的加載圖片,並沒有發現什麼問題。但是在測試的時候發現了問題。當多次打開軟件中一個浏覽大圖片的Activity(每次的圖片都不一樣)後,這時在這個Activity中點擊返回後,屏幕會突然變黑,然後回到了MainActivity,並沒有回到上一級Activity,甚至連之間打開的多個Activity都沒有返回。想了一下問題懷疑可能是重啟了這個應用。但那個大圖片的Activity開始打開的幾次都可以正常的回到上一級Activity,再多打開幾次就不能正常回退了,這時候我就開始看LogCat打出的日志,看看有沒有發生什麼異常。發現ImageLoader產生了OOM的Bug。如下圖:

OOMvc+086Osvs3Du9PQs6LK1FZvbGxleb/yvNyho7u509DIy8u1ysfTps6qyrnTw8HLY2FjaGVJbk1lbW9yeaOodHJ1ZaOpus1jYWNoZU9uRGlzY6OodHJ1ZaOp1eLBvbj2yvTQ1LW81sK1xKOstavKx87SvavL+8PHyejOqmZhbHNlu7nKx7vh0v23ok9PTaOsy/nS1L7N19S8utPDuaS+37fWzvahozxiciAvPg0KvMjIu7L6yfrBy09PTaOsztLDx77N0qq31s72xNq05qOsv7S/tL7fzOXKx8qyw7TUrdLytbzWwrXEoaPKudPDPGEgY2xhc3M9"replace_word" href="http://lib.csdn.net/base/15" target="_blank" title="undefined">AndroidStudio工具(發現很多地方都比eclipse強大),在使用AndroidStudio的Memory工具觀察的時候發現了問題,在我多次打開這個Activity的時候,發現Memory在一直的增長,每次Activity退出後Memory也沒有下降。

內存上升圖

就這樣當我多次浏覽不同的圖片,多次打開這個Activity後,這個Memory就一直的增長,當增長到120+M的時候應用突然掛掉,之後又自動重啟。發現了這個現象我們就可以借助AndroidStudio的強大工具來分析導致這個問題的原因。在Memory窗口的左邊有四個按鈕,分別是:
Enabled(藍色的開關):就是一個正常的開關功能
Initiate GC(橙色小卡車):就是手動調用GC,我們在抓內存前,一定要手動點擊 Initiate GC按鈕手動觸發GC,這樣抓到的內存使用情況就是不包括Unreachable對象的(Unreachable指的是可以被垃圾回收器回收的對象,但是由於沒有GC發生,所以沒有釋放,這時抓的內存使用中的Unreachable就是這些對象)
Dump Java Heap(紫色帶向下的箭頭):獲取hprof文件(hprof文件是我們使用MAT工具分析內存時使用的文件),但這裡直接產生的文件MAT還不能直接使用,需用轉換成標准的hprof文件。可以使用AndroidStudio轉換或者用hprof-conv命令轉化,網上可以查到。
Start Allocation Tracking(紫色帶圓圈):開始分配追蹤,第一次點擊可以指定追蹤內存的開始位置,第二次點擊可以結束追蹤的位置。這樣我們截取了一段要分析的內存,等待幾秒鐘AndroidStudio會給我們打開一個Allocation視圖(感覺和MAT工具差不多,不過MAT工具更加強大,我們也可以獲取hprof文件,使用MAT來分析)例如下圖:

我截取了這段內存,接下來我們就看Allocation視圖來分析。

\

通過視圖中的內容我們首先看到1號線程它占用了百分之92的內存,那麼我們就點開它看一下,每次都只要點開其中內存占用最大的就可以。

\

到劃紅線的那個位置就可以了,這裡已經到了安卓的graphics源碼的層次,所以我們不需要再向下看了。我們看一下紅線上面的ImageLoader裡的那個decodeStream方法:
decodeStream

它裡面就是調用了BitmapFactory.decoceStream()方法然後返回了一個Bitmap對象。基本上已經可以確定占用如此多內存的就是Bitmap對象,那麼我們只要將這個Bitmap釋放掉就可以了。那麼就接著分析看看在什麼地方釋放,我們看一下最上面的ImageLoader.core包下的LoadAndDisplayImageTask類的run()方法,發現裡面有一個Bitmap對象,他是調用了內部tryLoadBitmap()方法給賦值的,而這個方法最後其實就是我們上面看到的調用的decodeStream方法返回的Bitmap。應為這之間的Bitmap都是方法裡的局部變量所以我們不用考慮這些Bitmap的釋放,那就接著向下看,發現這個Bitmap被傳到了DisplayBitmapTask這個類中,那麼我們再進入到這個類中看發現了一個驚喜:

DisplayBitmapTask

這個對象被傳到了DisplayBitmapTask的成員變量中,DisplayBitmapTask本身也實現了Runnable接口,那麼我就想是不是應為DisplayBitmapTask這個類沒有及時回收導致的Bitmap成員變量占用內存的問題,所以我就在run()方法跑完之後,讓Bitmap = null。然後又將程序運行了一遍,但是發現根本沒有解決這個問題。這時候又重頭想了一下整個過程,發現我的操作是不對的,雖然我成功將DisplayBitmapTask中的Bitmap成員變量置為了空,但是他只是真正的Bitmap內存的一個引用,我並沒有將真正的Bitmap內存空間所釋放,只是將他的一個引用給釋放了。通過網上查詢發現,Bitmap會保存在C層的內存中,如果我們想要釋放他在C層中的內存,可以調用Bitmap的recycle()方法,從代碼中看,這個方法本身會調用Bitmap.cpp中的Bitmap_recycle方法,這個是native方法。這樣就可以通知底層將C層中的Bitmap內存釋放掉,而且還會將一些相關的引用計數置0。但是並不會立即釋放掉,這要看系統。 一下子完美解決。既然查到了原因,那就好辦了,我將Bitmap = null這句話改為Bitmap.recycle()。好了,接下來我再次運行,發現這回應用直接就崩了,只要能在Logcat中抓到Log,一切問題都好解決。看了一下log發現下面這個Error:

error

通過字面理解的意思就是,我們的View去顯示了已經recycle的Bitmap。仔細想一想確實是,我們在ImageLoader中的一個線程的run方法中直接將Bitmap釋放掉了,那我們的應用豈不是直接顯示一個空的Bitmap,這當然會引起錯誤。既然不能在這裡釋放掉Bitmap,那我們應該在什麼地方釋放呢?那一定是在我們的View不再使用這個Bitmap的時候調用,想一想,那我們只要在Activity或者fragment的onDestory()方法中釋放了不就可以了嗎?但這時又有一個問題產生,我們該如何在上層應用獲取到這個View用到的Bitmap的對象呢?想了半天發現好像只能通過改寫ImageLoader的源碼來想辦法將這個對象會傳到上層應用。正當我在ImageLoader中尋找在哪裡寫這個回調方法時,突然我看到了這段代碼:

這裡寫圖片描述

發現其實ImageLoader的源碼中已經為我們寫好了這個回調監聽器接口,而且在ImageLoader的displayImage方法中,也重載了一個提供了傳遞ImageLoaderListener實現對象的方法。這樣我們只需要實現ImageLoaderListener這個接口,並實現他的onLoadingComplete方法,在這個方法中對Bitmap做處理就可以了。

這裡寫圖片描述

我們只需要在Activity或Fragment的onDestory()方法中再調用cleanBitmapList()方法就可以了。通過上面的更改我再次運行應用,發現真的解決了這個問題,我的應用內存不再會是一直的上升,而是上升之後又會下降。如圖:

這裡寫圖片描述

注:這個方式比較適合cacheInMemory(false)和cacheOnDisc(false)的情況寫,應為如果你將這兩個設置為true時,那麼下次再次加載圖片時他會從內存和硬盤中去加載圖片,這兩個地方也持有Bitmap的引用,這時就會再次出現trying to use a recycled bitmap 這個錯誤。當然你也是可以設置為true的,只要在清除Bitmap(也就是我們這裡調用cleanBitmapList)的地方再加上mImageLoader.clearDiscCache()和mImageLoader.clearMemoryCache()這兩句話就可以。

好了,整個ImageLoader引起的OOM問題額分析與解決的過程就是這些,希望能對需要的人有幫助,通過這個問題我也學會到,遇到問題最好的解決辦法就是我們要從最根本源碼的角度去分析,這樣既能學到很多東西,又可以解決困難問題。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved