概念
- 內(nèi)存泄露 memory leak,是指程序在申請(qǐng)內(nèi)存后骡和,無(wú)法釋放已申請(qǐng)的內(nèi)存空間箍鼓。
對(duì)象被更長(zhǎng)生命周期的對(duì)象持有引用崭参,導(dǎo)致無(wú)法被GC回收】羁В【無(wú)用對(duì)象無(wú)法被回收】 - 內(nèi)存溢出 out of memory阵翎,是指程序在申請(qǐng)內(nèi)存時(shí)逢并,沒(méi)有足夠的內(nèi)存空間供其使用
影響
- 無(wú)用對(duì)象無(wú)法及時(shí)釋放,占用內(nèi)存
- 嚴(yán)重時(shí)造成內(nèi)存溢出
常見(jiàn)情況分析
Android多發(fā)生于Activity郭卫、Fragment等對(duì)象在生命周期結(jié)束后不能及時(shí)釋放
1. 單例模式
常見(jiàn)于單例模式的工具類需要持有Context對(duì)象砍聊,此情況下如果直接傳Activity對(duì)象來(lái)進(jìn)行初始化,會(huì)造成單例一直持有此Activity引用贰军,無(wú)法釋放玻蝌。
public class Singleton {
private static Singleton sInstance;
private Context mContext;
private Singleton (Context context){
this.mContext = context;
}
public static Singleton newInstance(Context context){
if(sInstance == null){
sInstance = new Singleton (context);
}
return sInstance;
}
}
解決方案:
- 如果單例模式必須持有context對(duì)象,需要使用與應(yīng)用生命周期一樣的Application對(duì)象
- 對(duì)于工具類必須使用到activity等短生命周期對(duì)象的情況词疼,可以選擇僅在操作方法中作為參數(shù)傳入俯树,這樣在方法執(zhí)行完成后會(huì)可以正常釋放
public class Singleton {
private static Singleton sInstance;
......
public void func(Activity activity){
//do something
}
}
2. 非靜態(tài)內(nèi)部類
由于非靜態(tài)內(nèi)部類、匿名內(nèi)部類等會(huì)持有外部類的引用贰盗,尤其需要注意使用Thread许饿、Runnable、AsyncTask等新開(kāi)線程的類時(shí)舵盈,常常習(xí)慣于直接使用匿名內(nèi)部類陋率,很容易造成外部類內(nèi)存泄漏。
class A{
private mB = new B();
//內(nèi)部類B持有外部類A的引用
class B{
//....
}
}
解決方案:
使用靜態(tài)內(nèi)部類或者外部類
3. Handler
Handler 造成內(nèi)存泄漏的原因也是持有內(nèi)部類持有外部類的引用秽晚。
public class TestActivity extends Activity {
int mCount =0;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//收到消息計(jì)數(shù)+1
super.handleMessage(msg);
mCount++;
}
};
//使用Handler 發(fā)送消息
public void send(){
Message message = Message.obtain();
mHandler.sendMessageDelayed(message, 1000);
}
}
mHandler是匿名內(nèi)部類而持有外部TestActivity引用瓦糟。
解決方案:
如果消息是延時(shí)發(fā)送(sendMessageDelay),Handler 里面的消息還沒(méi)發(fā)送完畢的話赴蝇,Activity 的內(nèi)存也不會(huì)被回收菩浙,需要移除消息。
改用靜態(tài)內(nèi)部類或外部類句伶。同時(shí)因?yàn)槭褂肏andler一般在收到消息后需要與Activity交互劲蜻,此時(shí)可以使用Activity的弱引用。
public class TestActivity extends Activity {
private Handler mHandler = new MyHandler(this);
//移除消息
@Override
onDestroy() {
mHandler.removeCallbacksAndMessages(null)
}
//靜態(tài)內(nèi)部類
static class MyHandler extends Handler {
//弱引用持有Activity
private WeakReference<Activity> mReference;
public MyHandler(Activity activity) {
mReference = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mWeakReference.get();
}
}
}
4. 集合持有子元素
集合會(huì)持有子元素引用考余,如果子元素屬于無(wú)用對(duì)象先嬉,應(yīng)該及時(shí)從集合清除
5. 資源沒(méi)有及時(shí)關(guān)閉
在使用完File,Cursor,Stream,Bitmap等資源時(shí),要及時(shí)關(guān)閉釋放內(nèi)存秃殉。
6. Rxjava內(nèi)存泄漏
通常使用RxJava做網(wǎng)絡(luò)加載時(shí)線程調(diào)度坝初,很可能由于子線程未執(zhí)行 完畢導(dǎo)致內(nèi)存泄漏浸剩。
解決方案:
- 使用CompositeDisposable管理
class Presenter {
private CompositeDisposable mDispose;
//訂閱事件天機(jī)到CompositeDisposable
private void addDispose(Disposable disposable) {
if(mDispose== null){
mDispose =new CompositeDisposable();
}
mDispose.add(disposable);
}
//生命周期結(jié)束前調(diào)用dispose方法
@Override
public void unbind(){
super.unbind();
mDispose.dispose();
mDispose = null;
}
//測(cè)試
public void funcTest() {
addDispose(Observable.just(1)
.subscribeWith(observer..));
}
}
7. MVP模式內(nèi)存泄漏
由于P層V層相互持有引用钾军,因此在V層(通常是Activity、Fragment等)生命周期結(jié)束前(如Activity.onDestroy時(shí))需要解除綁定
總結(jié)
內(nèi)存泄漏重點(diǎn)是無(wú)用的對(duì)象被生命周期較長(zhǎng)的對(duì)象引用導(dǎo)致GC時(shí)無(wú)法釋放绢要。
解決的辦法主要是從
- 短生命周期周期對(duì)象盡量避免持有長(zhǎng)生命周期對(duì)象引用
- 必須持有的吏恭,及時(shí)移除對(duì)長(zhǎng)生命周期對(duì)象的引用
- 利用弱引用的特性
- 及時(shí)關(guān)閉Cursor、File等資源