概述:
記憶體洩露是指當一個對象不再使用的時候,本該被回收,而被其他對象所持有導緻該對象無法被GC回收,這種導緻了本該被回收的對象不能被回收而停留在堆記憶體中,就産生了記憶體洩漏。
記憶體洩漏與記憶體溢出的差別
記憶體洩漏(Memory Leak)
程序中某些對象已經沒有使用的價值了,但是他們卻還可以直接或間接地被引用到GC Root導緻無法回收。當記憶體洩漏過多的時候,再加上應用本身占用的記憶體,最終可能就會導緻記憶體溢出OOM。
記憶體溢出(OOM)
當應用的heap資源超過了Dalvik虛拟機配置設定的記憶體就會記憶體溢出
記憶體洩漏帶來的影響
應用卡頓
應用異常(oom)
開發過程中經常遇到的:
1.單例造成的記憶體洩露
錯誤寫法:
Single single=Single.getInstance(this);
當調用getInstance時,如果傳入的context是Activity的context,隻有這個單例沒有被釋放,那麼這個activity就會一直持有這個引用,直到程序退出才會釋放。
public class Single {
private static Single instance;
private Context context;
private Single(Context context) {
this.context = context;
}
public static Single getInstance(Context context) {
if (instance == null) {
synchronized (Single.class) {
if (instance == null)
instance = new Single(context);
}
}
return instance;
}
}
改進後寫法:
Single single=Single.getInstance(getApplicationContext());
盡量使用Application的Context就不要使用Activity的Content,Application的生命周期伴随着整個程序的周期。
2.資源未關閉造成的記憶體洩漏
錯誤寫法:
對于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等資源的使用,應該在Activity銷毀時及時關閉或者登出,否則這些資源将不會被回收,造成記憶體洩漏。
改進後寫法:
在Activity銷毀時及時關閉或者登出。
3.非靜态内部類建立靜态執行個體造成的記憶體洩漏
錯誤寫法:
private static NoStaticMethod method;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (method!=null)
method=new NoStaticMethod();
}
class NoStaticMethod{
}
改進後寫法:
将非靜态内部類修改為靜态内部類。(靜态内部類不會隐式持有外部類)
4.Handelr造成的記憶體洩露
錯誤寫法:
mHandler是Handler的非靜态匿名内部類的執行個體,是以它持有外部類Activity的引用,我們知道消息隊列是在一個Looper線程中不斷輪詢處理消息,那麼當這個Activity退出時消息隊列中還有未處理的消息或者正在處理消息,而消息隊列中的Message持有mHandler執行個體的引用,mHandler又持有Activity的引用,是以導緻該Activity的記憶體資源無法及時回收,引發記憶體洩漏。
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
改進後寫法:
建立一個靜态Handler内部類,然後對Handler持有的對象使用弱引用,這樣在回收時也可以回收Handler持有的對象,這樣雖然避免了Activity洩漏,不過Looper線程的消息隊列中還是可能會有待處理的消息,是以我們在Activity的Destroy時或者Stop時應該移除消息隊列中的消息。
public static class MyHandler extends Handler {
private WeakReference<MainActivity> weakReference;
public MyHandler(MainActivity activity) {
weakReference = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = weakReference.get();
if (activity != null) {
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
5.線程造成的記憶體洩漏
錯誤寫法:
異步任務和Runnable都是一個匿名内部類,是以它們對目前Activity都有一個隐式引用。如果Activity在銷毀之前,任務還未完成, 那麼将導緻Activity的記憶體資源無法回收,造成記憶體洩漏。
new Thread(new Runnable() {
@Override
public void run() {
while (true){
Log.d(TAG, "--------------");
}
}
});
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
return null;
}
}.execute();
改進後寫法:
使用 靜态内部類,避免了Activity的記憶體資源洩漏,當然在Activity銷毀時候也應該取消相應的任務AsyncTask::cancel(),避免任務在背景執行浪費資源。
private static class MyThread extends Thread {
private WeakReference<MainActivity> mWeak;
public MyThread(MainActivity activity) {
mWeak = new WeakReference<MainActivity>(activity);
}
@Override
public void run() {
super.run();
while (true) {
if (mWeak == null) return;
if (mWeak.get() != null) {
Log.d(TAG, "--------------");
}
}
}
}
6.注冊了系統的服務,但onDestory未登出
AS檢視記憶體洩露
1.我們故意寫一個記憶體洩露的代碼,即:
Single.getInstance(getApplicationContext());
運作程式并退出(finish)

進行分析
我們可以看出是Sinle類出現了問題,原因我們也知道就是因為單例的問題,現在我們改一下代碼:
Single.getInstance(getApplicationContext());
再看:
果然沒有問題了
小結
以上這些隻是一些常見的造成記憶體洩露的原因,在實踐的路上我們還任重而道遠,我們可以通過AndroidStudio自帶的工具來檢查是否存在記憶體洩露,也可以通過添加第三方庫來檢查 。