以前用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實例,我們明明是全部退出的,怎么還存在,這表明出現了內存泄露。
內存泄漏產生的原因在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沒關系了。