Android studio 分析內存泄漏

之前用eclipse的時候,咱們採用的是DDMS和MAT,不只使用步驟複雜繁瑣,並且要手動排查內存泄漏的位置,操做起來比較麻煩。後來隨着Android studio的潮流,我也拋棄了eclipse加入了AS。android

Android Studio也開始支持自動進行內存泄漏檢查,而且操做起來也比較方便。緩存

咱們你們都知道,系統是不可能將全部的內存都分配給咱們的應用程序的。每一個程序都會有可以使用的內存上限,這被稱爲堆大小(Heap Size)。不一樣的手機,堆大小也不盡相同,隨着如今硬件設備不斷提升,堆大小也提高了。若是你們想要知道本身手機的堆大小是多少,能夠調用以下代碼:eclipse

ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); int heapSize = manager.getMemoryClass();  

結果是以MB爲單位進行返回的,咱們在開發應用程序時所使用的內存不能超出這個限制,不然就會出現OutOfMemoryError。所以,好比說咱們的程序中須要緩存一些數據,就能夠根據堆大小來決定緩存數據的容量。oop

下面介紹下采用Android Studio Monitor來檢測內存泄漏,栗子以下:this

每次啓動MainActivity中時都會調用一個線程,而後這個線程會執行runnable的run方法 因爲Runnable是一個匿名內部對象 因此握有MainActivity的引用,所以 
連續啓動MainActivity 4次,最後退出。按照常理來講,MainActivity會被銷燬回收,可實際可能並非這樣。atom

打開Android Studio,編譯代碼,在模擬器或者真機上運行App,在Android Monitor下點擊Monitor對應的Tab,進入以下界面spa

 

在Memory一欄中,能夠觀察不一樣時間App內存的動態使用狀況,點擊能夠手動觸發GC,點擊能夠進入HPROF Viewer界面,查看Java的Heap,切換到Package Tree View,方便查看,點擊Analyzer Task,Android Monitor就能夠爲咱們自動分析泄漏的Activity,以下圖線程

Reference Tree表明指向該實例的引用,能夠從這裏面查看內存泄漏的緣由,Shallow Size指的是該對象自己佔用內存的大小,Retained Size表明該對象被釋放後,垃圾回收器能回收的內存總和。code

看上圖,左邊是內存中的對象,右邊還存在4個MainActivity實例,咱們明明是所有退出的,怎麼還存在,這代表出現了內存泄露。對象

咱們查看Reference Tree, 看到 this$0個MainActivitythis$0是表示內部類的意思,也就是一個內部類引用了MainActivity 而 this$0又被 target引用, target是一個線程。內存泄漏的緣由就是MainActivity被內部類引用,而內部類又被線程使用,所以沒法釋放形成內存泄露。
 

內存泄漏產生的緣由在Android中大體分爲如下幾種:

1.static變量引發的內存泄漏 
由於static變量的生命週期是在類加載時開始 類卸載時結束,也就是說static變量是在程序進程死亡時才釋放,若是在static變量中 引用了Activity 那麼 這個Activity因爲被引用,便會隨static變量的生命週期同樣,一直沒法被釋放,形成內存泄漏。

解決辦法: 
在Activity被靜態變量引用時,使用 getApplicationContext 由於Application生命週期從程序開始到結束,和static變量的同樣。

2.線程形成的內存泄漏 
相似於上述例子中的狀況,線程執行時間很長,及時Activity跳出還會執行,由於線程或者Runnable是Acticvity內部類,所以握有Activity的實例(由於建立內部類必須依靠外部類),所以形成Activity沒法釋放。 
AsyncTask 有線程池,問題更嚴重

解決辦法: 
1.合理安排線程執行的時間,控制線程在Activity結束前結束。 
2.將內部類改成靜態內部類,並使用弱引用WeakReference來保存Activity實例 由於弱引用 只要GC發現了 就會回收它 ,所以可儘快回收

3.BitMap佔用過多內存 
bitmap的解析須要佔用內存,可是內存只提供8M的空間給BitMap,若是圖片過多,而且沒有及時 recycle bitmap 那麼就會形成內存溢出。

解決辦法: 
及時recycle 壓縮圖片以後加載圖片

4.資源未被及時關閉形成的內存泄漏 
好比一些Cursor 沒有及時close 會保存有Activity的引用,致使內存泄漏

解決辦法: 
在onDestory方法中及時 close便可

5.Handler的使用形成的內存泄漏 
因爲在Handler的使用中,handler會發送message對象到 MessageQueue中 而後 Looper會輪詢MessageQueue 而後取出Message執行,可是若是一個Message長時間沒被取出執行,那麼因爲 Message中有 Handler的引用,而 Handler 通常來講也是內部類對象,Message引用 Handler ,Handler引用 Activity 這樣 使得 Activity沒法回收。

解決辦法: 
依舊使用 靜態內部類+弱引用的方式 可解決

6.帶參數的單例

若是咱們在在調用Singleton的getInstance()方法時傳入了Activity。那麼當instance沒有釋放時,這個Activity會一直存在。所以形成內存泄露。
解決方法:

能夠將new Singleton(context)改成new Singleton(context.getApplicationContext())便可,這樣便和傳入的Activity不要緊了。

相關文章
相關標籤/搜索