正文
回到頂部對于C++來說,內存泄漏就是new出來的對象沒有delete,俗稱野指針;而對于java來說,就是new出來的Object放在Heap上無法被GC回收;而這里就把我之前的一篇內存泄漏的總結翻新,做一個更加全面規范的講解,希望能幫到各位。
回到頂部a) 靜態存儲區:編譯時就分配好,在程序整個運行期間都存在。它主要存放靜態數據和常量;
b) 棧區:當方法執行時,會在棧區內存中創建方法體內部的局部變量,方法結束后自動釋放內存;
c) 堆區:通常用來存放new出來的對象。由java垃圾回收期回收。
a) 強引用(StrongReference):JVM 寧可拋出 OOM ,也不會讓 GC 回收具有強引用的對象;
b) 軟引用(SoftReference):只有在內存空間不足時,才會被回的對象;
c) 弱引用(WeakReference):在 GC 時,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存;
d) 虛引用(PhantomReference):任何時候都可以被GC回收,當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是否存在該對象的虛引用,來了解這個對象是否將要被回收。可以用來作為GC回收Object的標志。

回到頂部
在這里先推薦兩種內存檢查的方式:
MAT(Memory Analyzer Tool),點我下載。具體使用這個網上一大篇,樓主一有時間也會為大家一步步奉上。強大的開源內存檢測工具LeakCanary。leakcanary是一個開源項目,一個內存泄露自動檢測工具,是著名的GitHub開源組織Square貢獻的,它的主要優勢就在于自動化過早的發覺內存泄露、配置簡單、抓取貼心,缺點在于還存在一些bug,不過正常使用百分之九十情況是OK的,其核心原理與MAT工具類似。
因為配置十分簡單,只需要幾句話就好!!!這里就不多說了,大家可以看官方文檔,簡單直白。
回到頂部
單例的使用在我們的程序中隨處可見,因為使用它可以完美的解決我們在程序中重復創建對象的問題,不過可別小瞧它。由于單例的靜態特性,使得它的生命周期和應用的生命周期會一樣長,所以一旦使用有誤,小心無限制的持有Activity的引用而導致內存泄漏。比如,下面的例子。
%201%20package%20com.example.nanchen.anrsolutiondemo.singleton;%202%20%203%20import%20android.content.Context;%204%20%205%20/**%206%20%20*%20@author%20nanchen%207%20%20*%20@fileName%20ANRSolutionDemo%208%20%20*%20@packageName%20com.example.nanchen.anrsolutiondemo.singleton%209%20%20*%20@date%202016/09/23%20%2011:2710%20%20*/11%20public%20class%20SingletonBad%20{12%20%20%20%20%20private%20static%20SingletonBad%20singletonBad;13%20%20%20%20%20private%20Context%20context;14%2015%20%20%20%20%20private%20SingletonBad(Context%20context){16%20%20%20%20%20%20%20%20%20this.context%20=%20context;17%20%20%20%20%20}18%2019%20%20%20%20%20public%20static%20SingletonBad%20getInstance(Context%20context){20%20%20%20%20%20%20%20%20%20if%20(singletonBad%20==%20null){21%20%20%20%20%20%20%20%20%20%20%20%20%20singletonBad%20=%20new%20SingletonBad(context);22%20%20%20%20%20%20%20%20%20}23%20%20%20%20%20%20%20%20%20return%20singletonBad;24%20%20%20%20%20}25%20}![]()
這個錯誤在生活中再普遍不過,很正常的一個單例模式,可就由于傳入的是一個Context,而這個Context的生命周期的長短就尤為重要了。如果我們傳入的是Activity的Context,當這個Context所對應的Activity退出的時候,由于該Context的引用被單例對象所持有,其生命周期等于整個應用程序的生命周期,所以當前Activity退出時它的內存并不會回收,這造成的內存泄漏就可想而知了。
正確的方式應該是把傳入的Context換為和應用的生命周期一樣長的application的Context;
![]()
%201%20package%20com.example.nanchen.anrsolutiondemo.singleton;%202%20%203%20import%20android.content.Context;%204%20%205%20/**%206%20%20*%20@author%20nanchen%207%20%20*%20@fileName%20ANRSolutionDemo%208%20%20*%20@packageName%20com.example.nanchen.anrsolutiondemo.singleton%209%20%20*%20@date%202016/09/23%20%2011:2910%20%20*/11%20public%20class%20SingletonGood%20{12%20%20%20%20%20private%20static%20SingletonGood%20singletonGood;13%20%20%20%20%20private%20Context%20context;14%2015%20%20%20%20%20private%20SingletonGood(Context%20context){16%20%20%20%20%20%20%20%20%20this.context%20=%20context.getApplicationContext();//獲取Application的context避免內存泄漏17%20%20%20%20%20}18%2019%20%20%20%20%20public%20static%20SingletonGood%20getInstance(Context%20context){20%20%20%20%20%20%20%20%20%20if%20(singletonGood%20==%20null){21%20%20%20%20%20%20%20%20%20%20%20%20%20singletonGood%20=%20new%20SingletonGood(context);22%20%20%20%20%20%20%20%20%20}23%20%20%20%20%20%20%20%20%20return%20singletonGood;24%20%20%20%20%20}25%20}![]()
當然,你也可以直接連Context都不用傳入了。重寫application,提供靜態的getContext方法
![]()
%201%20package%20com.example.nanchen.anrsolutiondemo.singleton;%202%20%203%20import%20android.app.Application;%204%20import%20android.content.Context;%205%20%206%20/**%207%20%20*%20@author%20nanchen%208%20%20*%20@fileName%20ANRSolutionDemo%209%20%20*%20@packageName%20com.example.nanchen.anrsolutiondemo.singleton10%20%20*%20@date%202016/09/23%20%2011:3211%20%20*/12%20public%20class%20DemoApplication%20extends%20Application%20{13%20%20%20%20%20private%20static%20DemoApplication%20demoApplication;14%2015%20%20%20%20%20public%20static%20DemoApplication%20getInstance(){16%20%20%20%20%20%20%20%20%20return%20demoApplication;17%20%20%20%20%20}18%2019%20%20%20%20%20public%20static%20Context%20getContext(){20%20%20%20%20%20%20%20%20%20return%20demoApplication.getApplicationContext();21%20%20%20%20%20}22%2023%20%20%20%20%20@Override24%20%20%20%20%20public%20void%20onCreate()%20{25%20%20%20%20%20%20%20%20%20super.onCreate();26%20%20%20%20%20%20%20%20%20demoApplication%20=%20this;27%20%20%20%20%20}28%20}![]()
自然就可以直接不用傳入Context
![]()
%201%20package%20com.example.nanchen.anrsolutiondemo.singleton;%202%20%203%20import%20android.content.Context;%204%20%205%20/**%206%20%20*%20@author%20nanchen%207%20%20*%20@fileName%20ANRSolutionDemo%208%20%20*%20@packageName%20com.example.nanchen.anrsolutiondemo.singleton%209%20%20*%20@date%202016/09/23%20%2011:3210%20%20*/11%20public%20class%20SingletonGoodNew%20{12%20%20%20%20%20private%20static%20SingletonGoodNew%20singletonGoodNew;13%2014%20%20%20%20%20private%20Context%20context;15%20%20%20%20%2016%20%20%20%20%20private%20SingletonGoodNew(){17%20%20%20%20%20%20%20%20%20this.context%20=%20DemoApplication.getContext();18%20%20%20%20%20}19%2020%20%20%20%20%20public%20static%20SingletonGoodNew%20getInstance(){21%20%20%20%20%20%20%20%20%20if%20(singletonGoodNew%20==%20null){22%20%20%20%20%20%20%20%20%20%20%20%20%20singletonGoodNew%20=%20new%20SingletonGoodNew();23%20%20%20%20%20%20%20%20%20}24%20%20%20%20%20%20%20%20%20return%20singletonGoodNew;25%20%20%20%20%20}26%20}![]()
2)令人心塞的Handler
%20 %20 %20 這個東西在我最近遇到的最多了,而它也是我們在內存泄漏中最為常見的,也許你的一個小忽略就會導致內存泄漏。在android的新版本中,我們被要求必須把網絡任務等耗時操作置于新線程來處理,我們通常會采用Handler。但Handler不是萬能的,若是我們的編寫不規范就有可能會造成內存泄漏。另外,我們知道,Handler、Message和MessageQueue都是相互關聯在一起的,萬一Handler發送的Message尚未被處理,則該Message及發送它的Handler對象將會被線程MessageQueue一直持有。
%20 %20 %20 由于Handler屬于TLS(Thread%20Local%20Storage)變量,生命周期和Activity是不一致的。因此這種實現方式一般很難保證跟View或者Activity的生命周期一致,故很容易導致無法正確釋放。比如:
![]()
%201%20package%20com.example.nanchen.anrsolutiondemo.handler;%202%20%203%20import%20android.os.Bundle;%204%20import%20android.os.Handler;%205%20import%20android.os.Message;%206%20import%20android.support.v7.app.AppCompatActivity;%207%20%208%20import%20com.example.nanchen.anrsolutiondemo.R;%209%2010%20public%20class%20HandlerBadActivity%20extends%20AppCompatActivity%20{11%2012%20%20%20%20%20private%20final%20Handler%20handler%20=%20new%20Handler(){13%20%20%20%20%20%20%20%20%20@Override14%20%20%20%20%20%20%20%20%20public%20void%20handleMessage(Message%20msg)%20{15%20%20%20%20%20%20%20%20%20%20%20%20%20super.handleMessage(msg);16%20%20%20%20%20%20%20%20%20}17%20%20%20%20%20};18%2019%20%20%20%20%20@Override20%20%20%20%20%20protected%20void%20onCreate(Bundle%20savedInstanceState)%20{21%20%20%20%20%20%20%20%20%20super.onCreate(savedInstanceState);22%20%20%20%20%20%20%20%20%20setContentView(R.layout.activity_handler_bad);23%2024%20%20%20%20%20%20%20%20%20//%20延遲10s發送一個消息25%20%20%20%20%20%20%20%20%20handler.postDelayed(new%20Runnable()%20{26%20%20%20%20%20%20%20%20%20%20%20%20%20@Override27%20%20%20%20%20%20%20%20%20%20%20%20%20public%20void%20run()%20{28%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20write%20something29%20%20%20%20%20%20%20%20%20%20%20%20%20}30%20%20%20%20%20%20%20%20%20},1000*60*5);31%2032%20%20%20%20%20%20%20%20%20this.finish();33%20%20%20%20%20}34%20}![]()
在例子中,我們申明了一個延遲5分鐘執行的消息Message。當該Activity被finish的時候,延遲任務的Message還存在于主線程中,它持有該Activity的Handler引用,所以此時Finish掉的Activity就不會回收了,所以造成了內存泄漏(因handler為非靜態內部類,它會持有外部類的引用,在這里就是當前的Activity)。
修復:這個解決也是可以通過把其聲明為static的,則其存活期就跟activity的生命周期無關了。不過倘若用到Context等外部類的非static對象,還是應該通過弱引用傳入。比如:
![]()
%201%20package%20com.example.nanchen.anrsolutiondemo.handler;%202%20%203%20import%20android.os.Bundle;%204%20import%20android.os.Handler;%205%20import%20android.os.Message;%206%20import%20android.support.v7.app.AppCompatActivity;%207%20%208%20import%20com.example.nanchen.anrsolutiondemo.R;%209%2010%20import%20java.lang.ref.WeakReference;11%2012%20public%20class%20HandlerGoodActivity%20extends%20AppCompatActivity%20{13%2014%20%20%20%20%20private%20static%20final%20class%20MyHandler%20extends%20Handler{15%20%20%20%20%20%20%20%20%20private%20final%20WeakReference<HandlerGoodActivity>%20mActivity;16%2017%20%20%20%20%20%20%20%20%20public%20MyHandler(HandlerGoodActivity%20activity){18%20%20%20%20%20%20%20%20%20%20%20%20%20this.mActivity%20=%20new%20WeakReference<HandlerGoodActivity>(activity);//使用弱引用19%20%20%20%20%20%20%20%20%20}20%2021%20%20%20%20%20%20%20%20%20@Override22%20%20%20%20%20%20%20%20%20public%20void%20handleMessage(Message%20msg)%20{23%20%20%20%20%20%20%20%20%20%20%20%20%20super.handleMessage(msg);24%20%20%20%20%20%20%20%20%20%20%20%20%20HandlerGoodActivity%20activity%20=%20mActivity.get();25%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(activity%20!=%20null){26%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20write%20something27%20%20%20%20%20%20%20%20%20%20%20%20%20}28%20%20%20%20%20%20%20%20%20}29%20%20%20%20%20}30%2031%20%20%20%20%20private%20final%20MyHandler%20myHandler%20=%20new%20MyHandler(this);32%2033%20%20%20%20%20//%20匿名內部類在static的時候絕對不會持有外部類的引用34%20%20%20%20%20private%20static%20final%20Runnable%20RUNNABLE%20=%20new%20Runnable()%20{35%20%20%20%20%20%20%20%20%20@Override36%20%20%20%20%20%20%20%20%20public%20void%20run()%20{37%2038%20%20%20%20%20%20%20%20%20}39%20%20%20%20%20};40%2041%20%20%20%20%20@Override42%20%20%20%20%20protected%20void%20onCreate(Bundle%20savedInstanceState)%20{43%20%20%20%20%20%20%20%20%20super.onCreate(savedInstanceState);44%20%20%20%20%20%20%20%20%20setContentView(R.layout.activity_handler_good);45%2046%20%20%20%20%20%20%20%20%20myHandler.postDelayed(RUNNABLE,1000*60*5);47%20%20%20%20%20}48%20}![]()
綜述:推薦使用靜態內部類+弱引用WeakReference這種方式,但要注意每次使用前判空。
說到若引用,這里再提下java的幾種引用類型:Strong%20reference,SoftReference,WeakReference和PhatomReference
![]()
在android開發中,為了防止內存溢出,在處理一些占用內存大并且生命周期較長的對象的時候,可以盡量地使用軟引用和弱引用技術。
比如,保存Bitmap的軟引用到HashMap。
![]()
%201%20package%20com.example.nanchen.anrsolutiondemo.photo;%202%20%203%20import%20android.graphics.Bitmap;%204%20import%20android.graphics.BitmapFactory;%205%20%206%20import%20java.lang.ref.SoftReference;%207%20import%20java.util.HashMap;%208%20%209%20/**10%20%20*%20@author%20nanchen11%20%20*%20@fileName%20ANRSolutionDemo12%20%20*%20@packageName%20com.example.nanchen.anrsolutiondemo.photo13%20%20*%20@date%202016/09/23%20%2011:5814%20%20*/15%20public%20class%20CacheBySoftRef%20{16%2017%20%20%20%20%20//%20首先定義一個HashMap,保存軟應用對象18%20%20%20%20%20private%20HashMap<String,SoftReference<Bitmap>>%20imageCache%20=%20%20new%20HashMap<>();19%20%20%20%20%20//%20再來定義一個方法,保存Bitmap的軟引用到HashMap20%20%20%20%20%20public%20void%20addBitmapToCache(String%20path){21%20%20%20%20%20%20%20%20%20//%20強引用的Bitmap對象22%20%20%20%20%20%20%20%20%20Bitmap%20bitmap%20=%20BitmapFactory.decodeFile(path);23%20%20%20%20%20%20%20%20%20//%20軟引用的Bitmap對象24%20%20%20%20%20%20%20%20%20SoftReference<Bitmap>%20softBitmap%20=%20new%20SoftReference<Bitmap>(bitmap);25%20%20%20%20%20%20%20%20%20//%20添加該對象到Map使其緩存26%20%20%20%20%20%20%20%20%20imageCache.put(path,softBitmap);27%20%20%20%20%20}28%2029%20%20%20%20%20//%20獲取的時候,可以通過SoftReference的get()的方法得到Bitmap對象30%20%20%20%20%20public%20Bitmap%20getBitmapByPath(String%20path){31%20%20%20%20%20%20%20%20%20//%20從緩存中取軟引用的Bitmap對象32%20%20%20%20%20%20%20%20%20SoftReference<Bitmap>%20softBitmap%20=%20imageCache.get(path);33%20%20%20%20%20%20%20%20%20//%20判斷是否存在軟引用34%20%20%20%20%20%20%20%20%20if%20(softBitmap%20==%20null){35%20%20%20%20%20%20%20%20%20%20%20%20%20return%20null;36%20%20%20%20%20%20%20%20%20}37%20%20%20%20%20%20%20%20%20//%20通過軟引用取出Bitmap對象,如果由于內存不足Bitmap被回收,則取得空;38%20%20%20%20%20%20%20%20%20//%20如果未被回收,則可重復使用,提高速度39%20%20%20%20%20%20%20%20%20Bitmap%20bitmap%20=%20softBitmap.get();40%20%20%20%20%20%20%20%20%20return%20bitmap;41%20%20%20%20%20}42%20}![]()
使用軟引用以后,在OutOfMemory異常發生之前,這些緩存的圖片資源的內存空間可以被釋放掉的,從而避免內存達到上限,避免Crash發生。
如果只是想避免OutOfMemory異常的發生,則可以使用軟引用。如果對于應用的性能更在意,想盡快回收一些占用內存比較大的對象,則可以使用弱引用。
另外可以根據對象是否經常使用來判斷選擇軟引用還是弱引用。如果該對象可能會經常使用的,就盡量用軟引用。如果該對象不被使用的可能性更大些,就可以用弱引用。
ok,繼續回到主題。前面所說的,創建一個靜態Handler內部類,然后對%20Handler%20持有的對象使用弱引用,這樣在回收時也可以回收%20Handler%20持有的對象,但是這樣做雖然避免了%20Activity%20泄漏,不過%20Looper%20線程的消息隊列中還是可能會有待處理的消息,所以我們在%20Activity%20的%20Destroy%20時或者%20Stop%20時應該移除消息隊列%20MessageQueue%20中的消息。
下面幾個方法都可以移除%20Message:
![]()
3)匿名內部類/非靜態內部類,它們方便卻暗藏殺機
android開發經常會繼承實現Activity或者Fragment或者View。如果你使用了匿名類,而又被異步線程所引用,那得小心,如果沒有任何措施同樣會導致內存泄漏的
![]()
%201%20package%20com.example.nanchen.anrsolutiondemo.innerClass;%202%20%203%20import%20android.os.Bundle;%204%20import%20android.support.v7.app.AppCompatActivity;%205%20%206%20import%20com.example.nanchen.anrsolutiondemo.R;%207%20%208%20public%20class%20MainActivity%20extends%20AppCompatActivity%20{%209%2010%20%20%20%20%20private%20static%20TestInnerBad%20testInnerBad%20=%20null;11%2012%20%20%20%20%20class%20TestInnerBad{13%2014%20%20%20%20%20}15%2016%20%20%20%20%20@Override17%20%20%20%20%20protected%20void%20onCreate(Bundle%20savedInstanceState)%20{18%20%20%20%20%20%20%20%20%20super.onCreate(savedInstanceState);19%20%20%20%20%20%20%20%20%20setContentView(R.layout.activity_inner_bad);20%2021%20%20%20%20%20%20%20%20%20if%20(testInnerBad%20==%20null){22%20%20%20%20%20%20%20%20%20%20%20%20%20testInnerBad%20=%20new%20TestInnerBad();23%20%20%20%20%20%20%20%20%20}24%2025%20%20%20%20%20%20%20%20%20Runnable%20runnable1%20=%20new%20MyRunnable();26%20%20%20%20%20%20%20%20%20Runnable%20runnable2%20=%20new%20Runnable()%20{27%20%20%20%20%20%20%20%20%20%20%20%20%20@Override28%20%20%20%20%20%20%20%20%20%20%20%20%20public%20void%20run()%20{29%2030%20%20%20%20%20%20%20%20%20%20%20%20%20}31%20%20%20%20%20%20%20%20%20};32%20%20%20%20%20}33%2034%20%20%20%20%20private%20static%20class%20MyRunnable%20implements%20Runnable{35%2036%20%20%20%20%20%20%20%20%20@Override37%20%20%20%20%20%20%20%20%20public%20void%20run()%20{38%2039%20%20%20%20%20%20%20%20%20}40%20%20%20%20%20}41%2042%20}![]()
runnable1%20和%20runnable2的區別就是,runnable2使用了匿名內部類,我們看看引用時的引用內存
![]()
可以看到,runnable1是沒有什么特別的。但runnable2多出了一個MainActivity的引用,若是這個引用再傳入到一個異步線程,此線程在和Activity生命周期不一致的時候,也就造成了Activity的泄露。
4)前輩箴言——善用static成員變量
前面就很明顯,當我們的成員變量是static的時候,那么它的生命周期將和整個app的生命周期一致。
這必然會導致一系列問題,如果你的app進程設計上是長駐內存的,那即使app切到后臺,這部分內存也不會被釋放。按照現在手機app內存管理機制,占內存較大的后臺進程將優先回收,因為如果此app做過進程互保保活,那會造成app在后臺頻繁重啟。當手機安裝了你參與開發的app以后一夜時間手機被消耗空了電量、流量,你的app不得不被用戶卸載或者靜默。
這里修復的方法是:
不要在類初始時初始化靜態成員。可以考慮lazy初始化(延遲加載)。架構設計上要思考是否真的有必要這樣做,盡量避免。如果架構需要這么設計,那么此對象的生命周期你有責任管理起來。
5)遠離非靜態內部類和匿名類,多用private static class。
在我們的日常代碼中,這樣的情況似乎很常見,及直接寫一個class就這么光禿禿的情況。

這樣就在Activity內部創建了一個非靜態內部類的單例,每次啟動Activity時都會使用該單例的數據,這樣雖然避免了資源的重復創建,不過這種寫法卻會造成內存泄漏,因為非靜態內部類默認會持有外部類的引用,而該非靜態內部類又創建了一個靜態的實例,該實例的生命周期和應用的一樣長,這就導致了該靜態實例一直會持有該Activity的引用,導致Activity的內存資源不能正常回收。正確的做法為:
將該內部類設為靜態內部類或將該內部類抽取出來封裝成一個單例,如果需要使用Context,請按照上面推薦的使用Application 的 Context。當然,Application 的 context 不是萬能的,所以也不能隨便亂用,對于有些地方則必須使用 Activity 的 Context,對于Application,Service,Activity三者的Context的應用場景如下:

其中: NO1表示 Application 和 Service 可以啟動一個 Activity,不過需要創建一個新的 task 任務隊列。而對于 Dialog 而言,只有在 Activity 中才能創建
6) 集合對象善清除,以免內存泄漏觸不及防
我們通常會把一些對象的引用加入到集合容器(比如ArrayList)中,當我們不再需要該對象時,并沒有把它的引用從集合中清理掉,這樣這個集合就會越來越大。如果這個集合是static的話,那情況就更嚴重了。
所以在退出程序之前,將集合里面的東西clear,然后置為null,再退出程序,如下:
![復制代碼]()
1 private List<String> nameList; 2 private List<Fragment> list; 3 4 @Override 5 public void onDestroy() { 6 super.onDestroy(); 7 if (nameList != null){ 8 nameList.clear(); 9 nameList = null;10 }11 if (list != null){12 list.clear();13 list = null;14 }15 }![復制代碼]()
7)webView雖火,內存泄漏卻也火的其所
當我們不再需要使用webView的時候,應該調用它的destory()方法來銷毀它,并釋放其占用的內存,否則其占用的內存長期也不能回收,從而造成內存泄漏。
解決方案:
為webView開啟另外一個進程,通過AIDL與主線程進行通信,webView所在的進程可以根據業務的需要選擇合適的時機進行銷毀,從而達到內存的完整釋放。
而另外一些諸如listView的Adapter沒有緩存之類的這里就不再多提了。
8)做一個小的總結
構造Adapter時,沒有使用緩存的 convertViewBitmap對象不在使用時調用recycle()釋放內存Context使用不當造成內存泄露:不要對一個Activity Context保持長生命周期的引用。盡量在一切可以使用應用ApplicationContext代替Context的地方進行替換。非靜態內部類的靜態實例容易造成內存泄漏:即一個類中如果你不能夠控制它其中內部類的生命周期(譬如Activity中的一些特殊Handler等),則盡量使用靜態類和弱引用來處理(譬如ViewRoot的實現)。警惕線程未終止造成的內存泄露;譬如在Activity中關聯了一個生命周期超過Activity的Thread,在退出Activity時切記結束線程。一個典型的例子就是HandlerThread的run方法是一個死循環,它不會自己結束,線程的生命周期超過了Activity生命周期,我們必須手動在Activity的銷毀方法中中調運thread.getLooper().quit();才不會泄露。對象的注冊與反注冊沒有成對出現造成的內存泄露;譬如注冊廣播接收器、注冊觀察者(典型的譬如數據庫的監聽)等。創建與關閉沒有成對出現造成的泄露;譬如Cursor資源必須手動關閉,WebView必須手動銷毀,流等對象必須手動關閉等。不要在執行頻率很高的方法或者循環中創建對象(比如onmeasure),可以使用HashTable等創建一組對象容器從容器中取那些對象,而不用每次new與釋放。避免代碼設計模式的錯誤造成內存泄露;譬如循環引用,A持有B,B持有C,C持有A,這樣的設計誰都得不到釋放。
回到頂部五、真相只有一個
內存泄漏實在太普遍了,真是防不勝防。不過我想告訴廣大的小伙伴,程序員嘛,誰還不踩個坑,跳出來,拍拍身上的灰塵,總結一下,過兩天又是一條幫幫的coder。你可以的。
新聞熱點
疑難解答