Android導致內存泄漏的幾種情形

集合類
集合類如果僅僅有添加元素的方法,而沒有相應的刪除機制,導致內存被佔用。如果這個集合類是全局性的變量(比如類中的靜態屬性,全局性的map等即有靜態引用或final一直指向它),那麼沒有相應的刪除機制,很可能導致集合所佔用的內存只增不減。

單例模式
不正確使用單例模式是引起內存泄漏的一個常見問題,單例對象在被初始化後將在JVM的整個生命週期中存在(以靜態變量的方式),如果單例對象持有外部對象的引用,那麼這個外部對象將不能被JVM正常回收,導致內存泄漏。
如果需要Context,儘量引用Application,而不用Activity。

Android組件或特殊集合對象的使用
BroadcastReceiver,ContentObserver,FileObserver,Cursor,Callback等在Activity onDestory或者某類生命週期結束之後一定要unregister或者close掉,否則這個Activity類會被system強引用,不會被內存回收。不要直接對Activity進行直接引用作爲成員變量,如果不得不這麼做,請用private WeakReference mActivity來做,相同的,對於Service等其他有自己生命週期的對象來說,直接引用都需要謹慎考慮是否會存在內存泄漏的可能。

Handler
Handler的生命週期與Activity不一致
由於Handler屬於TLS(Thread Local Storage)變量,生命週期和Activity是不一致的。
當Android應用啓動的時候,會先創建一個UI主線程的Looper對象,Looper實現了一個簡單的消息隊列,一個一個的處理裏面的Message對象。主線程Looper對象在整個應用生命週期中存在。
當在主線程中初始化Handler時,該Handler和Looper的消息隊列關聯(沒有關聯會報錯的)。發送到消息隊列的Message會引用發送該消息的Handler對象,這樣系統可以調用 Handler#handleMessage(Message) 來分發處理該消息。
只要Handler發送的Message尚未被處理,則該Message及發送它的Handler對象將被線程MessageQueue一直持有。因此這種實現方式一般很難保證跟View或者Activity的生命週期保持一致,故很容易導致無法正確釋放。

handler引用Activity阻止了GC對Acivity的回收
在Java中,非靜態(匿名)內部類會默認隱性引用外部類對象。而靜態內部類不會引用外部類對象。
如果外部類是Activity,則會引起Activity泄露。
當Activity finish後,延時消息會繼續存在主線程消息隊列中1分鐘,然後處理消息。而該消息引用了Activity的Handler對象,然後這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導致該Activity對象無法被回收,從而導致了上面說的 Activity泄露。

如上所述,Handler的使用要尤爲小心,否則將很容易導致內存泄漏的發生。

錯誤示例:

private final Handler mHandler = new Handler() { 
 @Override          
 public void handleMessage(Message msg) {              
 // ...         
 }     
};

解決辦法:
使用顯形的引用,1.靜態內部類。 2. 外部類
使用弱引用 2. WeakReference

正確示例:

private static class MyHandler extends Handler {         
 private final WeakReference<HandlerActivity2> mActivity;          
 public MyHandler(HandlerActivity2 activity) {  
  mActivity = new WeakReference<HandlerActivity2>(activity);              
 }          
 @Override     
 public void handleMessage(Message msg) {      
 if (mActivity.get() == null) {                  
 return;              
 }              
 mActivity.get().todo();     
     }   
  @Override    
  public void onDestroy() {  
      //  If null, all callbacks and messages will be removed.       
         mHandler.removeCallbacksAndMessages(null); 
           }

http://blog.csdn.net/zhuanglonghai/article/details/38233069

ThreadHandler
使用ThreadHandler步驟:
http://www.cnblogs.com/hnrainll/p/3597246.html
創建一個HandlerThread,即創建了一個包含Looper的線程

HandlerThread handlerThread = new HandlerThread("test");
handlerThread.start(); //創建HandlerThread後一定要記得start()
//獲取HandlerThread的Looper

Looper looper = handlerThread.getLooper();
//創建Handler,通過Looper初始化

Handler handler = new Handler(looper);

通過以上三步我們就成功創建HandlerThread。通過handler發送消息,就會在子線程中執行。
如果我們在Activity的onCreate中進行上面的初始化,不再進行其他工作,那麼就有可能造成內存泄漏。
因爲不過Activity finish後,進程沒有退出,那麼創建的test會一直存在。

解決辦法:
在onDestory中調用handlerThread.quit()或handlerThread.quitSafely();

Thread內存泄漏
線程也是造成內存泄漏的一個重要的源頭。線程產生的內存泄漏主要原因在於線程生命週期的不可控。比如線程是Activity的內部類,則線程對象中保存了Activity的一個引用,當線程的run函數耗時較長沒有結束時,線程對象是不會被銷燬的,因此它引用的老的Activity也不會被銷燬,因此就出現了內存泄漏的問題。

一些不良代碼造成的內存壓力
這些代碼並不造成內存泄漏,但是它們,或是對沒使用的內存沒有進行有效及時的釋放,或是沒有有效的利用已有的對象而是頻繁的申請新內存。

Bitmap沒調用recycle()
Bitmap對象在不使用時,我們應該先調用recycle()釋放內存,然後將它設置爲null。因爲加載Bitmap對象的內存空間,一部分是java的,一部分C的(因爲Bitmap分配的底層是通過JNI調用的)。而這個recycle()就是針對C部分的內存釋放。

構造Adapter時,沒有使用緩存的convertView。
http://blog.csdn.net/u010878994/article/details/51553415

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章