java垃圾回收器,開發者無需特意管理內存分配,降低了應用由於局部故障導致崩潰,同時防止未釋放的內存把堆棧擠爆的可能,所以寫出的代碼更為安全。
但是,在java中仍存在很多容易導致內存泄漏的邏輯可能。如果不小心,則很容易浪費掉未釋放的內存,最終導致內存用光的錯誤拋出OOM
內存泄漏
- 一般內存泄漏(traditional memory leak):由忘記釋放分配的內存導致的(Cursor忘記關閉等)
- 邏輯內存泄漏(logical memory leak):當應用不再需要這個對象,但仍未釋放該對象的所有引用
如果持有對象的強引用,垃圾回收器是無法在內存中回收這個對象
Android 中,最容易引發的內存泄漏為 Context,如Activity的Context,就包含了大量的內存引用,如View Hierarchies和其他資源。一旦泄漏Context,就意味着泄漏它指向的所有對象。android機器內存有限,太多的內存泄漏容易導致OOM
Activity.onDestroy()被視為Activity生命的結束,程序上看來,它應該被銷毀,或者Android系統需要回收這些內存(當內存不夠時,android會回收看不見的activity)
如果Activity.onDestroy()執行完,堆棧中仍存在持有該activity的引用,垃圾回收器就無法把他標記成已回收的內存(結果就是Activity存活在它的生命周期之外)
導致潛在的內存泄漏的陷阱不外乎兩種:
- 全局進程(process-global)的static變量。這個無視應用狀態,持有activity的強引用的東西
- 活在Activity生命周期之外的線程。沒有清空對Activity的強引用。
----------------------------------------------------------------------------------------------------------------
Static Activity
在類中定義了靜態Activity變量,把當前運行的activity實例賦值於這個靜態變量。
如果這個靜態變量在activity生命結束后沒有清空,就會導致內存泄漏。因為static貫穿了這個應用的生命周期,所以被泄露的Activity就會一直存在於應用的進程中,不會被垃圾回收器回收。
避免這種代碼 static Activity activity;
----------------------------------------------------------------------------------------------------------------
Static Views 單例中保存activity
在單例中,如果Activity經常被用到,那么內存中保存一個實例是很實用的。但由於單例的生命周期是應用程序的生命周期,這樣做會延長Activity的生命周期(強制延長Activity的生命周期是相當危險且不必要的,無論如何都不能在單例中保存類似的Activity對象)
特殊情況:如果一個View初始化耗費大量資源,而且在一個activity生命周期內保持不變,那可以把它變成static,加載到視圖樹上(View Hierachy),像這樣,當activity被銷毀時,應當釋放資源。(在銷毀視圖時應該把這個static view的view置為null)
在調用Singleton的getInstance()方法時傳入了Activity,那么當instance 沒有釋放時,這個Activity會一直存在。因此造成內存泄漏。
可以在傳入context時傳入context.getApplicationContext()即可
static view也不建議,或者應該在銷毀時置null
Inner Classes:提高可讀性,但是導致內存泄漏的原因,就是內部持有外部類實例的強引用,如內部類中持有Activity對象
解決辦法,1.將內部類變為靜態內部類,把匿名內部類變為靜態匿名內部類
2.如果有強引用Activity中的屬性,則將該屬性的引用方式改為弱引用
3.在業務運行的情況下,當Activity執行onDestroy時,結束這些耗時任務
----------------------------------------------------------------------------------------------------------------
Inner Classes
假設Activity中有個內部類,這樣做可以提高可讀性和封裝性。假如我們創建一個內部類,而且持有一個靜態變量的引用,那么很有可能內存泄漏(銷毀的時候置空即可)
內部類的優勢之一就是可以訪問外部類,不好的是,導致內存泄漏的原因,就是因為內部類持有外部類實例的引用。
----------------------------------------------------------------------------------------------------------------
Anonymous Classes
匿名類也維護了外部類的引用。所以內存泄漏很容易發生,當你在Activity中定義了匿名的AsyncTask。當陰補任務在后台執行耗時任務期間,Activity不幸被銷毀(用戶退出,系統回收),這個被AsyncTask持有的Activity實例就不會被垃圾回收器回收,直到異步任務結束。
----------------------------------------------------------------------------------------------------------------
Handler
同樣,定義匿名的Runnable,用匿名Handler執行。Runnable內部類會持有外部類的隱式引用,被傳遞到Handler的消息隊列MessageQueue中,在Message消息沒有被處理之前,Activity實例不會被銷毀,於是導致了內存泄漏。
----------------------------------------------------------------------------------------------------------------
Threads
只要是匿名類的實例,不管是不是在工作線程,都會持有Activity的引用,導致內存泄漏
----------------------------------------------------------------------------------------------------------------
TimerTask
只要是匿名類的實例,不管是不是在工作線程,都會持有Activity的引用,導致內存泄漏
----------------------------------------------------------------------------------------------------------------
Sensor Manager
通過Context.getSystemService(int name) 可獲取系統服務。這些服務工作在各自的進程中,幫助應用處理后台任務,處理硬件交互。如果需要使用這些服務,可注冊監聽器,這會導致服務持有了Context的引用,如果在Activity銷毀的時候沒有注銷這些監聽,會導致內存泄漏