一、非靜態內部類造成的內存泄漏
造成原因:非靜態內部類默認會持有外部類的引用,如果內部類的生命周期超過了外部類就會造成內存泄漏。
場景:當Activity銷毀后,由于內部類中存在異步耗時任務還在執行,導致Activity實例一直被內部類持有無法被回收,造成內存泄漏
例如:
//TestActivity
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {new Thread(new Runnable() {@Overridepublic void run() {try {//模擬耗時Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();}});
此時點擊返回鍵關閉當前Activity,頁面正常回退,但匿名內部類中還有耗時任務執行,如果耗時任務執行完之后要刷新頁面還會造成異常導致app閃退。
解決辦法:不使用匿名內部類,并且使用static關鍵字修飾內部類(static修飾的內部類不持有外部類的引用,也自然不會造成上面的內存泄漏),如果內部類中需要使用外部類的資源,可以使用弱引用的方式持有外部類。
優化后的代碼如下:
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {new Thread(new MyRunnable()).start();}});static class MyRunnable implements Runnable{@Overridepublic void run() {try {//模擬耗時Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
二、靜態成員變量造成的內存泄漏
造成原因:靜態成員變量的生命周期 = 應用程序的生命周期,如果該靜態成員應用的變量生命周期 < 改靜態變量則會造成內存泄漏
場景:靜態成員變量持有了一個耗費資源過多的實例(Activity,Fragment)
例如:
public class Person {private static Activity mActivity;Person(Activity activity){mActivity = activity;}
}
此時點擊返回鍵關閉當前Activity,但Person類中的mActivity靜態變量持有當前Activity的引用,導致改Activity對象本該被回收而沒被回收,導致內存泄漏
解決辦法:
- 盡量避免
Static
成員變量引用資源耗費過多的實例(如Context) - 如果必須使用Context,可以使用Application的Context
- 使用弱引用代替強引用持有
三、單例模式造成的內存泄漏
造成原因:單例模式由于其具有靜態特性,導致其生命周期 = 應用程序生命周期,如果單例中持有別的類的實例,就會造成內存泄漏
場景:單例模式中持有一個耗費資源過多的實例(Context)
例如:
public class SingleInstance{private static SingleInstance instance;private Context mContext;private SingleInstance(Context context) {this.mContext = context; // 傳遞的是Activity的context}public SingleInstance getInstance(Context context) {if (instance == null) {instance = new SingleInstance(context);}return instance;}
}
此時由于單例中持有傳入的Activity實例,倒是該Activity關閉時,資源得不到回收,從而造成內存泄漏
解決辦法:
使用Application的Context代替Activity的Contex
四、Handler造成的內存泄漏
造成原因:當使用非靜態內部類(包含匿名內部類)創建Handler時,Handler會持有外部類的對象,如果Handler中還有消息沒執行完,此時創建Handler的Activity關閉就會造成內存泄漏。通常由于子線程持有handler的引用(因為要發消息給handler來更新界面),handler又持有activity的引用,從而導致activity不能正常被回收,造成內存泄漏
場景:Activity中通過一個子線程異步請求網絡數據,請求成功后更新當前頁面。
例如:
//MainActivity.javaprivate Handler mHandler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(Message msg) {//更新頁面}};//成功獲取到網絡數據更新頁面private void handleData(String data){Message message = Message.obtain();message.obj = data;mHandler.sendMessage(message);
}
解決辦法:
1、靜態內部類+弱引用
static class MyHandler extends Handler {WeakReference<Activity > mReference;MyHandler(Activity activity) {mReference= new WeakReference<Activity>(activity);}@Overridepublic void handleMessage(Message msg) {final Activity activity = mReference.get();if (activity != null) {//更新頁面}}
}
2、activity銷毀時,及時清理消息
@Override
protected void onDestroy() {super.onDestroy();if (mHandler != null) {mHandler.removeCallbacksAndMessages(null);}
}
五、多線程造成的內存泄漏
造成原因:上面一和四其實也是多線程造成內存泄漏的場景,主要是子線程中持有外部類(例如Activity)的引用,讓后子線程的生命周期又和Activity不同步,從而造成activity被銷毀時子線程中的任務還在執行,從而導致activity的資源遲遲得不到回收造成內存泄漏。
六、集合類造成的內存泄漏
造成原因:當我們把一些對象的引用加入到集合對象(例如常用的ArrayList),當我們不需要改對象時,沒有從集合中清理掉改對象的引用,這樣集合就會越來越大,如果集合時static的那問題就更嚴重了。
場景:學生管理系統中需要統計學生信息,使用一個集合存儲學生信息,信息統計完成,把信息存入數據庫后就不需要學生對象信息了,但是沒及時清理掉集合,導致學生對象得不到回收,造成內存泄漏
例如:
// 通過 循環申請Person 對象并放入集合
List<Person> personList = new ArrayList<>();for (int i = 0; i < 10; i++) {Person p = new Person();personList.add(p);
// 雖釋放了集合元素引用的本身:p=null
// 但集合List仍然吃藥后該對象引用,所以依然不可回收該對象p = null;
}
解決辦法:在不使用改集合時,清理集合并把集合置為空
// 釋放personList
personList.clear();
personList=null;
七、WebView造成的內存泄漏
造成原因:WebView中可能會進行大量的網絡請求,加載大量的資源,從而使得內存占用過高,當我們推出承載Webview的Activity時,沒有正確的釋放WebView中的資源,導致內存泄漏。
例如:新聞列表頁面使用WebView加載一個h5頁面,此時會進行大量網絡請求加載新聞圖片內容,當我們回退該頁面時,加載的資源沒正確釋放導致內存泄漏
解決方案:不使用xml定義webview,通過代碼的形式,傳入Application的Context然后在承載webview的頁面銷毀時,釋放webview的資源
//代碼初始化Webview
mWebView=new WebView(getApplicationContext());
LinearLayout linearLayout = findViewById(R.id.webview);
linearLayout.addView(mWebView);@Override
protected void onDestroy() {if( mWebView!=null) {// 如果先調用destroy()方法,則會命中if (isDestroyed()) return;這一行代碼,需要先onDetachedFromWindow(),再// destory()ViewParent parent = mWebView.getParent();if (parent != null) {((ViewGroup) parent).removeView(mWebView);}mWebView.stopLoading();// 退出時調用此方法,移除綁定的服務,否則某些特定系統會報錯mWebView.getSettings().setJavaScriptEnabled(false);mWebView.clearHistory();mWebView.clearView();mWebView.removeAllViews();mWebView.destroy();}super.on Destroy();
}
八、資源未釋放造成的內存泄漏
造成原因:對于資源的使用(如 廣播BraodcastReceiver
、文件流File
、數據庫游標Cursor
、圖片資源Bitmap
等),若在Activity
銷毀時無及時關閉 / 注銷這些資源,則這些資源將不會被回收,從而造成內存泄漏
解決方案: 在Activity
銷毀時 及時關閉 / 注銷資源
關閉資源代碼如下:
// 對于 廣播BraodcastReceiver:注銷注冊
unregisterReceiver()// 對于 文件流File:關閉流
InputStream / OutputStream.close()// 對于數據庫游標cursor:使用后關閉游標
cursor.close()// 對于 圖片資源Bitmap:Android分配給圖片的內存只有8M,若1個Bitmap對象占內存較多,當它不再被使用時,應調用recycle()回收此對象的像素所占用的內存;最后再賦為null
Bitmap.recycle();
Bitmap = null;// 對于動畫(屬性動畫)
// 將動畫設置成無限循環播放repeatCount = “infinite”后
// 在Activity退出時記得停止動畫
參考連接:WebView內存泄漏–解決方法小結 - 簡書 (jianshu.com)
https://juejin.cn/post/6844904067534159880