內存空間使用完畢以後未回收, 會致使內存泄漏。有人會問:Java不是有垃圾自動回收機制麼?不幸的是,在Java中仍存在不少容易致使內存泄漏的邏輯(logical leak)。雖然垃圾回收器會幫咱們幹掉大部分無用的內存空間,可是對於還保持着引用,但邏輯上已經不會再用到的對象,垃圾回收器不會回收它們。
例如java
Android內存回收管理策略圖:android
圖中的每一個圓節點表明對象的內存資源,箭頭表明可達路徑。當圓節點與 GC Roots 存在可達路徑時,表示當前資源正被引用,虛擬機是沒法對其進行回收的(如圖中的黃色節點)。反過來,若是圓節點與 GC Roots 不存在可達路徑,則意味着這塊對象的內存資源再也不被程序引用,系統虛擬機能夠在 GC 過程當中將其回收掉。git
從定義上講,Android(Java)平臺的內存泄漏是指沒有用的對象資源任與GC-Root保持可達路徑,致使系統沒法進行回收。github
好比Activity的Context
,就包含大量的內存引用,一旦泄漏了Context
,也意味泄漏它指向的全部對象。app
形成Activity泄漏的常見緣由:異步
在類中定義了靜態Activity變量,把當前運行的Activity實例賦值於這個靜態變量。
若是這個靜態變量在Activity生命週期結束後沒有清空,就致使內存泄漏。
由於static變量是貫穿這個應用的生命週期的,因此被泄漏的Activity就會一直存在於應用的進程中,不會被垃圾回收器回收。ide
static Activity activity; //這種代碼要避免
在單例模式中,若是Activity常常被用到,那麼在內存中保存一個Activity實例是很實用的。可是因爲單例的生命週期是應用程序的生命週期,這樣會強制延長Activity的生命週期,這是至關危險並且沒必要要的,不管如何都不能在單例子中保存相似Activity的對象。
舉例:函數
public class Singleton { private static Singleton instance; private Context mContext; private Singleton(Context context){ this.mContext = context; } public static Singleton getInstance(Context context){ if (instance == null){ synchronized (Singleton.class){ if (instance == null){ instance = new Singleton(context); } } } return instance; } }
在調用Singleton的getInstance()方法時傳入了Activity。那麼當instance沒有釋放時,這個Activity會一直存在。所以形成內存泄露。
解決方法:工具
能夠將new Singleton(context)改成new Singleton(context.getApplicationContext())便可,這樣便和傳入的Activity不要緊了。
同理,靜態的View也是不建議的gradle
內部類的優點能夠提升可讀性和封裝性,並且能夠訪問外部類,不幸的是,致使內存泄漏的緣由,就是內部類持有外部類實例的強引用。 例如在內部類中持有Activity對象
解決方法:
1.將內部類變成靜態內部類;
2.若是有強引用Activity中的屬性,則將該屬性的引用方式改成弱引用;
3.在業務容許的狀況下,當Activity執行onDestory時,結束這些耗時任務;
例如:
發生內存泄漏的代碼:
public class LeakAct extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); test(); } //這兒發生泄漏 public void test() { new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
解決方法:
public class LeakAct extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); test(); } //加上static,變成靜態匿名內部類 public static void test() { new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
匿名類也維護了外部類的引用。當你在匿名類中執行耗時任務,若是用戶退出,會致使匿名類持有的Activity實例就不會被垃圾回收器回收,直到異步任務結束。
handler中,Runnable內部類會持有外部類的隱式引用,被傳遞到Handler的消息隊列MessageQueue中,在Message消息沒有被處理以前,Activity實例不會被銷燬了,因而致使內存泄漏。
解決方法:
1.能夠把Handler類放在單獨的類文件中,或者使用靜態內部類即可以免泄露;
2.若是想在Handler內部去調用所在的Activity,那麼能夠在handler內部使用弱引用的方式去指向所在Activity.使用Static + WeakReference的方式來達到斷開Handler與Activity之間存在引用關係的目的.
3.在界面銷燬是,釋放handler資源
@Override protected void doOnDestroy() { super.doOnDestroy(); if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); } mHandler = null; mRenderCallback = null; }
線程產生內存泄露的主要緣由在於線程生命週期的不可控。若是咱們的線程是Activity的內部類,因此MyThread中保存了Activity的一個引用,當MyThread的run函數沒有結束時,MyThread是不會被銷燬的,所以它所引用的老的Activity也不會被銷燬,所以就出現了內存泄露的問題。
由於非靜態內部類會自動持有一個所屬類的實例,若是所屬類的實例已經結束生命週期,但內部類的方法仍在執行,就會hold其主體(引用)。也就使主體不能被釋放,亦即內存泄露。靜態類編譯後和非內部類是同樣的,有本身獨立的類名。不會悄悄引用所屬類的實例,因此就不容易泄露。
Bitmap對象在不使用時,咱們應該先調用recycle()釋放內存,而後才設置爲null.
咱們一般把一些對象的引用加入到了集合中,當咱們不須要該對象時,並無把它的引用從集合中清理掉,這樣這個集合就會愈來愈大。若是這個集合是static的話,那狀況就更嚴重了。
解決方案:
在Activity退出以前,將集合裏的東西clear,而後置爲null,再退出程序。
這種Android的內存泄露比純Java的內存泄漏還要嚴重,由於其餘一些Android程序可能引用系統的Android程序的對象(好比註冊機制)。即便Android程序已經結束了,可是別的應用程序仍然還有對Android程序的某個對象的引用,泄漏的內存依然不能被垃圾回收。
解決方案:
1.使用ApplicationContext代替ActivityContext;
2.在Activity執行onDestory時,調用反註冊;
資源性對象好比(Cursor,File文件等)每每都用了一些緩衝,咱們在不使用的時候,應該及時關閉它們,以便它們的緩衝及時回收內存。而不是等待GC來處理。
由於Bitmap佔用的內存實在是太多了,特別是分辨率大的圖片,若是要顯示多張那問題就更顯著了。Android分配給Bitmap的大小隻有8M.
解決方法:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2;//圖片寬高都爲原來的二分之一,即圖片爲原來的四分之一
//軟引用 SoftReference<Bitmap> bitmap = new SoftReference<Bitmap>(pBitmap); //回收操做 if(bitmap != null) { if(bitmap.get() != null && !bitmap.get().isRecycled()){ bitmap.get().recycle(); bitmap = null; } }
解決方案:
用新的進程起含有WebView的Activity,而且在該Activity 的onDestory() 最後加上 System.exit(0); 殺死當前進程。
Lint 是 Android Studio 自帶的工具,使用姿式很簡單 Analyze -> Inspect Code 而後選擇想要掃面的區域便可
對可能引發泄漏的編碼,Lint 都會進行舒適提示:
Square 公司出品的內存分析工具,官方地址以下:https://github.com/square/lea...
LeakCanary 須要在項目代碼中集成,不過代碼也很是簡單,以下的官方示例:
在你的 build.gradle:
dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3' // Optional, if you use support library fragments: debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3' }
在 Application 類:
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } LeakCanary.install(this); // Normal app init code... } }
當內存泄漏發生時,LeakCanary 會彈窗提示並生成對應的堆存儲信息記錄
-3.Android Monitor
開Android Studio,編譯代碼,在模擬器或者真機上運行App,而後點擊
,在Android Monitor下點擊Monitor對應的Tab,進入以下界面
在Memory一欄中,能夠觀察不一樣時間App內存的動態使用狀況,點擊
能夠手動觸發GC,點擊
能夠進入HPROF Viewer界面,查看Java的Heap,以下圖
Reference Tree表明指向該實例的引用,能夠從這裏面查看內存泄漏的緣由,Shallow Size指的是該對象自己佔用內存的大小,Retained Size表明該對象被釋放後,垃圾回收器能回收的內存總和。