做了較長時間的android開發了,發現其實android應用開發入門容易,但是進階或者成為進階工程師,需要具備的基礎能力還是非常高的:效能最佳化、記憶體泄露、apk瘦身、熱修複等等,這些都非常的考驗一個人的能力。android成長之路還很長,自己會持續的走下去。本文主要介紹android記憶體泄露方面的知識。其實要真的理解記憶體泄露,需要對JVM、java語言有一定的瞭解,在這個基礎上就比較容易理解本文了。
一.記憶體泄露概念
在java中,如果一個對象沒有可用價值了,但又被其他引用所指向,那麼這個對象對於gc來說就不是一個垃圾, 所以不會對其進行回收,但是我們認為這應該是個垃圾,應該被gc回收的。這個對象得不到gc的回收, 就會一直存活在堆記憶體中,佔用記憶體,就跟我們說的霸著茅坑不拉屎的道理是一樣的。這樣就導致了記憶體的泄露。
為什麼會記憶體泄露呢,根本原因就是一個永遠不會被使用的對象,因為一些引用沒有斷開,沒有滿足GC條件,導致不會被回收,這就造成了記憶體泄露。比如在Activity中註冊了一個廣播接收器,但是在頁面關閉的時候進行unRegister,就會出現記憶體溢出的現象。如果我們的java運行很久,而這種記憶體泄露不斷的發生,最後就沒記憶體可用了,最終就是我們看到的OOM錯誤。雖然android的記憶體泄露做到了應用程式層級的泄露(android中的每個應用程式都是獨立運行在單獨進程中的,每個應用進程都由虛擬機器指定了一個記憶體上限值,一旦記憶體佔用值超過這個上限值,就會發生oom錯誤,進程被強制kill掉,kill掉的進程記憶體會被系統回收),但是對於一名開發工程師,絕對不能放過任何的記憶體泄露。
二.出現記憶體泄露的場合及解決方案
1.activity中handler間斷的post訊息,會造成activity泄露
1.1出現場合:
{ Handler mLeakyHandler = Handler() { (Message msg) { } } (Bundle savedInstanceState) { .onCreate(savedInstanceState); mLeakyHandler.postDelayed( Runnable() { () { } }, * * ); }}
1.2原因分析:
當這個Activity被finished後,延時發送的訊息會繼續在主線程的訊息佇列中存活10分鐘,直到他們被處理。這個訊息持有這個Activity的Handler引用,這個Handler有隱式地持有他的外部類(在這個例子中是SampleActivity)。直到訊息被處理前,這個引用都不會被釋放。因此Activity不會被記憶體回收機制回收,泄露他所持有的應用程式資源。注意,第15行的匿名Runnable類也一樣。匿名類的非靜態執行個體持有一個隱式的外部類引用,因此context將被泄露。
具體的引用順序:
MessageQueue->Message->Runnable->Handler->Activity。所以這就導致當前activity與MessageQueue一直有關聯,導致LeakActivity的對象不能被gc回收,從而導致記憶體泄露。
1.3解決方案:
為瞭解決這個問題,Handler的子類應該定義在一個新檔案中或使用靜態內部類。靜態內部類不會隱式持有外部類的引用。所以不會導致它的Activity泄露。如果你需要在Handle內部調用外部Activity的方法,那麼讓Handler持有一個Activity的弱引用(WeakReference)以便你不會意外導致context泄露。為瞭解決我們執行個體化匿名Runnable類可能導致的記憶體泄露,我們將用一個靜態變數來引用他(因為匿名類的靜態執行個體不會隱式持有他們外部類的引用)。
{ Runnable sRunnable = Runnable() { () { } }; MyHandler mHandler = MyHandler(); (Bundle savedInstanceState) { .onCreate(savedInstanceState); mHandler.postDelayed(sRunnable, * * ); finish(); } { WeakReference<SampleActivity> mActivity; (SampleActivity activity) { mActivity = WeakReference<SampleActivity>(activity); } (Message msg) { SampleActivity activity = mActivity.get(); (activity != ) { } } }}