Android內(nèi)存管理:內(nèi)存泄漏

目錄

簡(jiǎn)單描述:沒(méi)有用的對(duì)象無(wú)法回收的現(xiàn)象就是內(nèi)存泄露

如果程序發(fā)生了內(nèi)存泄露,則會(huì)帶來(lái)以下這些問(wèn)題

  • 應(yīng)用可用的內(nèi)存減少,增加了堆內(nèi)存的壓力
  • 降低了應(yīng)用的性能,比如會(huì)觸發(fā)更頻繁的 GC
  • 嚴(yán)重的時(shí)候可能會(huì)導(dǎo)致內(nèi)存溢出錯(cuò)誤,即 OOM Error
    OOM 發(fā)生很有可能是內(nèi)存泄露導(dǎo)致的,但并非所有的 OOM 都是由內(nèi)存泄露引起的,內(nèi)存泄露也并不一定引起 OOM。

一、Java 的內(nèi)存管理和引用類型

你真的懂 Java 的內(nèi)存管理和引用類型嗎?

二、Android 中內(nèi)存泄露的常見(jiàn)場(chǎng)景 & 解決方案

1、單例模式強(qiáng)引用Context造成的內(nèi)存泄露

過(guò)多的單例會(huì)讓內(nèi)存占用過(guò)多,而且單例模式由于其 靜態(tài)特性,其生命周期 = 應(yīng)用程序的生命周期,不正確地使用單例模式也會(huì)造成內(nèi)存泄露。

單例模式
public class SingleInstanceTest {

    private static SingleInstanceTest sInstance;
    private Context mContext;

    private SingleInstanceTest(Context context){
        this.mContext = context;
    }

    public static SingleInstanceTest newInstance(Context context){
        if(sInstance == null){
            sInstance = new SingleInstanceTest(context);
        }
        return sInstance;
    }
}

上面是一個(gè)比較簡(jiǎn)單的單例模式用法,需要外部傳入一個(gè) Context 來(lái)獲取該類的實(shí)例,如果此時(shí)傳入的 Context 是 Activity 的話,此時(shí)單例就有持有該 Activity 的強(qiáng)引用(直到整個(gè)應(yīng)用生命周期結(jié)束)。這樣的話,即使該 Activity 退出,該 Activity 的內(nèi)存也不會(huì)被回收,這樣就造成了內(nèi)存泄露,特別是一些比較大的 Activity,甚至還會(huì)導(dǎo)致 OOM(Out Of Memory)。

解決方法: 使用 getApplicationContext() 單例模式引用的對(duì)象的生命周期 = 應(yīng)用生命周期

public class SingleInstanceTest {

    private static SingleInstanceTest sInstance;
    private Context mContext;

    private SingleInstanceTest(Context context){
        this.mContext = context.getApplicationContext();
    }

    public static SingleInstanceTest newInstance(Context context){
        if(sInstance == null){
            sInstance = new SingleInstanceTest(context);
        }
        return sInstance;
    }
}
2、普通類強(qiáng)引用Context造成的內(nèi)存泄露
public class Sample {
    
    private Context mContext;
    
    public Sample(Context context){
        this.mContext = context;
    }

    public Context getContext() {
        return mContext;
    }
}

// 外部調(diào)用
Sample sample = new Sample(MainActivity.this);

解決方法: 使用 弱引用 WeakReference

public class Sample {

    private WeakReference<Context> mWeakReference;

    public Sample(Context context){
        this.mWeakReference = new WeakReference<>(context);
    }

    public Context getContext() {
        if(mWeakReference.get() != null){
            return mWeakReference.get();
        }
        return null;
    }
}

被弱引用關(guān)聯(lián)的對(duì)象只能存活到下一次垃圾回收之前,也就是說(shuō)即使 Sample 持有 Activity 的引用,但由于 GC 會(huì)幫我們回收相關(guān)的引用,被銷(xiāo)毀的 Activity 也會(huì)被回收內(nèi)存,這樣我們就不用擔(dān)心會(huì)發(fā)生內(nèi)存泄露了。

3、非靜態(tài)內(nèi)部類 / 匿名類
4、靜態(tài)集合類 / 靜態(tài)變量 / static
   static List<Object> objectList = new ArrayList<>();
   for (int i = 0; i < 10; i++) {
       Object obj = new Object();
       objectList.add(obj);
       obj = null;
    }

靜態(tài)變量的生命周期和應(yīng)用程序一致,而且他們所引用的對(duì)象 Object 也不能釋放,這樣便造成了內(nèi)存泄露。

解決方法:在集合元素使用之后從集合中刪除,等所有元素都使用完之后,將集合置空。

    objectList.clear();
    objectList = null;

三、內(nèi)存泄露排查工具

1、Android Lint

Lint 是 Android Studio 提供的 代碼掃描分析工具,它可以幫助我們發(fā)現(xiàn)代碼機(jī)構(gòu) / 質(zhì)量問(wèn)題,同時(shí)提供一些解決方案,檢測(cè)內(nèi)存泄露當(dāng)然也不在話下,使用也是非常的簡(jiǎn)單,可以參考下這篇文章:Android 性能優(yōu)化:使用 Lint 優(yōu)化代碼、去除多余資源

2、leakcanary

LeakCanary 是 Square 公司開(kāi)源的「Android 和 Java 的內(nèi)存泄漏檢測(cè)庫(kù)」,Square 出品,必屬精品,功能很強(qiáng)大,使用也很簡(jiǎn)單。建議直接看 Github 上的說(shuō)明:leakcanary,也可以參考這篇文章:Android內(nèi)存優(yōu)化(六)LeakCanary使用詳解


參考資料

Android 關(guān)于內(nèi)存泄露,你必須了解的東西

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容