Android效能優化篇之記憶體優化--記憶體洩漏
文章目錄
介紹
本文主要介紹記憶體洩漏的產生原因分析,常見的導致記憶體洩漏的示例,以及記憶體洩漏優化的方法1
什麼是記憶體洩露
當一個物件已經不需要在使用了,本應該被回收,而另一個正在使用的物件持有它的引用,導致物件不能被回收。因為不能被及時回收的本該被回收的記憶體,就產生了記憶體洩漏。如果記憶體洩漏太多會導致程式沒有辦法申請記憶體,最後出現記憶體溢位的錯誤。
android中導致記憶體洩漏的主要幾種情況
- 使用單例模式
- 使用匿名內部類
- 使用非同步事件處理機制Handler
- 使用靜態變數
- 資源未關閉
- 設定監聽
- 使用AsyncTask
- 使用Bitmap
1.單例模式
單例模式是我們在開發過程中經常使用的一種設計模式,但是使用的不當也會造成記憶體洩露的產生,下面舉個例子
private static ComonUtil mInstance = null; private Context mContext = null; public ComonUtil(Context context) { mContext = context; } public static ComonUtil getInstance(Context context) { if (mInstance == null) { mInstance = new ComonUtil(context); } return mInstance; }
我們看到上面的程式碼就是我們平時使用的單例模式,當然這裡沒有考慮執行緒安全,請忽略。當我們傳遞進來的是Context,那麼當前物件就會持有第一次例項化的Context,如果Context是Activity物件,那麼就會產生記憶體洩漏。因為當前物件ComonUtil是靜態的,生命週期和應用是一樣的,只有應用退出才會釋放,導致Activity不能及時釋放,帶來記憶體洩漏。
怎麼解決呢?
常見的有兩種方式,第一就是傳入ApplicationContext,第二CommonUtil中取context.getApplicationContext()。
public ComonUtil(Context context) {
mContext = context.getApplicationContext();
}
2.使用非靜態內部類
/**
* 非靜態內部類
*/
public void createNonStaticInnerClass(){
CustomThread mCustomThread = new CustomThread();
mCustomThread.start();
}
public class CustomThread extends Thread{
@Override
public void run() {
super.run();
while (true){
try {
Thread.sleep(5000);
Log.i(TAG,"CustomThread ------- 列印");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
我們就以執行緒為例,當Activity呼叫了createNonStaticInnerClass方法,然後退出當前Activity時,因為執行緒還在後臺執行且當前執行緒持有Activity引用,只有等到執行緒執行完畢,Activitiy才能得到釋放,導致記憶體洩漏。
常用的解決方法有很多,第一把執行緒類宣告為靜態的類,如果要用到Activity物件,那麼就作為引數傳入且為WeakReference,第二在Activity的onDestroy時,停止執行緒的執行。
public static class CustomThread extends Thread{
private WeakReference<MainActivity> mActivity;
public CustomThread(MainActivity activity){
mActivity = new WeakReference<MainActivity>(activity)
}
}
3.使用非同步事件處理機制Handler
/**
* 非同步訊息處理機制 -- handler機制
*/
public void createHandler(){
mHandler.sendEmptyMessage(0);
}
public Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//處理耗時操作
return false;
}
});
這個應該是我們平時使用最多的一種方式,如果當handler中處理的是耗時操作,或者當前訊息佇列中訊息很多時,那當Activity退出時,當前message中持有handler的引用,handler又持有Activity的引用,導致Activity不能及時的釋放,引起記憶體洩漏的問題。
解決handler引起的記憶體洩漏問題常用的兩種方式:
1.和上面解決Thread的方式一樣,
2.在onDestroy中呼叫mHandler.removeCallbacksAndMessages(null)
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
4.使用靜態變數
同單例引起的記憶體洩漏。
5.資源未關閉
常見的就是資料庫遊標沒有關閉,物件檔案流沒有關閉,主要記得關閉就OK了。
6.設定監聽
常見的是在觀察者模式中出現,我們在退出Acviity時沒有取消監聽,導致被觀察者還持有當前Activity的引用,從而引起記憶體洩漏。
常見的解決方法就是在onPause中注消監聽
7.使用AsyncTask
public AsyncTask<Object, Object, Object> mTask = new AsyncTask<Object, Object, Object>() {
@Override
protected Object doInBackground(Object... params) {
//耗時操作
return null;
}
@Override
protected void onPostExecute(Object o) {
}
};
和上面同樣的道理,匿名內部類持有外部類的引用,AsyncTask耗時操作導致Activity不能及時釋放,引起記憶體洩漏。
解決方法同上:
1.宣告為靜態類,
2.在onPause中取消任務
8.使用Bitmap
我們知道當bitmap物件沒有被使用(引用),gc會回收bitmap的佔用記憶體,當時這邊的記憶體指的是java層的,那麼本地記憶體的釋放呢?我們可以通過呼叫bitmap.recycle()來釋放C層上的記憶體,防止本地記憶體洩漏
轉載說明:
連結:https://www.jianshu.com/p/797395731747 ↩︎