Android studio 分析內存泄漏


以前用eclipse的時候,我們采用的是DDMS和MAT,不僅使用步驟復雜繁瑣,而且要手動排查內存泄漏的位置,操作起來比較麻煩。后來隨着Android studio的潮流,我也拋棄了eclipse加入了AS。

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

我們大家都知道,系統是不可能將所有的內存都分配給我們的應用程序的。每個程序都會有可使用的內存上限,這被稱為堆大小(Heap Size)。不同的手機,堆大小也不盡相同,隨着現在硬件設備不斷提高,堆大小也提升了。如果大家想要知道自己手機的堆大小是多少,可以調用如下代碼:

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

結果是以MB為單位進行返回的,我們在開發應用程序時所使用的內存不能超出這個限制,否則就會出現OutOfMemoryError。因此,比如說我們的程序中需要緩存一些數據,就可以根據堆大小來決定緩存數據的容量。

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

每次啟動MainActivity中時都會調用一個線程,然后這個線程會執行runnable的run方法 由於Runnable是一個匿名內部對象 所以握有MainActivity的引用,因此 
連續啟動MainActivity 4次,最后退出。按照常理來說,MainActivity會被銷毀回收,可實際可能並不是這樣。

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

 

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

Reference Tree代表指向該實例的引用,可以從這里面查看內存泄漏的原因,Shallow Size指的是該對象本身占用內存的大小,Retained Size代表該對象被釋放后,垃圾回收器能回收的內存總和。

看上圖,左邊是內存中的對象,右邊還存在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沒關系了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM