Android—常見的內(nèi)存泄漏場景及解決方案

我的CSDN: ListerCi
我的簡書: 東方未曦

一、引言

一般情況下Android的內(nèi)存泄漏是因為,存在引用指向一個本該被回收的對象豺鼻,例如已經(jīng)執(zhí)行onDestroy()的Activity。在這種情況下,由于Activity內(nèi)某些對象的生命周期比Activity要長噩斟,在Activity理論上被銷毀時,該對象依舊存在并持有Activity的引用孤个,因此內(nèi)存回收機(jī)制(GC)無法釋放Activity剃允,最終導(dǎo)致內(nèi)存泄漏。

為了發(fā)現(xiàn)和修復(fù)APP中存在的內(nèi)存泄漏齐鲤,開發(fā)人員會在APP上安裝內(nèi)存檢測工具(如leakcanary)斥废,當(dāng)出現(xiàn)內(nèi)存泄漏時,該工具會提供一個報告给郊,里面包含了一條引用鏈营袜,指明可能造成內(nèi)存泄漏的引用。開發(fā)人員需要在合適的地方切斷引用鏈丑罪,以便GC釋放掉沒有被引用的對象荚板。

有些內(nèi)存泄漏的修復(fù)很簡單,將非靜態(tài)內(nèi)部類內(nèi)部類改為靜態(tài)內(nèi)部類或者將Context改為ApplicationContext后檢測工具就檢測不出內(nèi)存泄漏了吩屹,但是這到底是為什么呢跪另?而且就算檢測工具檢測不出內(nèi)存泄漏,就真的萬無一失了嗎煤搜?

帶著這些問題免绿,我們來分析一下Android常見的內(nèi)存泄漏場景以及解決方案。

二擦盾、Java內(nèi)存管理及垃圾回收機(jī)制

在了解Android的內(nèi)存泄漏之前嘲驾,我們需要先了解Java的內(nèi)存管理以及垃圾回收機(jī)制。

2.1 內(nèi)存管理

Java的內(nèi)存分配區(qū)域主要分為以下幾個部分迹卢。

1. 靜態(tài)變量區(qū)

用于存儲被static修飾的靜態(tài)變量辽故,這塊區(qū)域在程序開始運(yùn)行時就已經(jīng)分配完畢,并且存在于程序的整個運(yùn)行過程腐碱。

2. 棧

主要用于分配局部變量誊垢,包括基本類型的變量和對象的引用變量,當(dāng)局部變量的作用域結(jié)束之后,Java會自動釋放掉該變量占用的內(nèi)存空間喂走。

3. 堆

堆是動態(tài)內(nèi)存區(qū)域殃饿,程序運(yùn)行期間新建的對象實例和數(shù)組都存儲在堆中,垃圾回收機(jī)制(GC)管理的就是這塊內(nèi)存芋肠。為了及時地將不被使用的對象釋放掉乎芳,GC需要監(jiān)控每一個對象的狀態(tài),當(dāng)一個對象不再被引用時帖池,GC就會釋放該對象奈惑。

4. 常量池

常量池中的內(nèi)容在編譯時就已經(jīng)確定,主要包含代碼中的基本類型和對象類型的常量值碘裕。
例如携取,String就是對象類型,如果在編譯時確定了String的值(String s = "test")帮孔,那么它的值就存儲在常量池中雷滋,而它的引用存儲在棧中。如果String的值是在程序運(yùn)行時確定的(String s = new String("..."))文兢,那么它的值就存儲在堆中晤斩。

假設(shè)當(dāng)前有一個實例A存儲在堆中,我們定義了一個引用a指向?qū)嵗鼳姆坚。此時引用a其實是保存在棧中的澳泵,它的值為實例A在堆內(nèi)存中的首地址,此時程序就可以通過a讀寫A的值兼呵。

2.2 垃圾回收機(jī)制

上面提到兔辅,當(dāng)一個對象不再被引用時,GC就應(yīng)該將其回收击喂。確實有一種引用計數(shù)法來判斷一個對象是否需要被釋放维苔,當(dāng)該對象的引用計數(shù)為0時代表它需要被回收。但是如果存在兩個對象懂昂,沒有別的引用指向它們介时,但是它們互相引用,此時它們的引用計數(shù)都不為0凌彬,導(dǎo)致無法釋放沸柔,容易造成內(nèi)存泄漏。

目前主流的的方法是通過可達(dá)性分析來判斷一個對象是否需要被釋放铲敛。該算法的基本思路就是通過一些被稱為引用鏈(GC Roots)的對象作為起點褐澎,從這些節(jié)點開始向下搜索,搜索走過的路徑被稱為(Reference Chain)原探,當(dāng)一個對象到GC Roots沒有任何引用鏈相連時(即從GC Roots節(jié)點到該節(jié)點不可達(dá))乱凿,則證明該對象是不可用的顽素。

在Java中咽弦,可作為GC Root的對象包括以下幾種:

  • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象
  • 方法區(qū)中類靜態(tài)屬性引用的對象
  • 方法區(qū)中常量引用的對象
  • 本地方法棧中JNI(即一般說的Native方法)引用的對象

三徒蟆、Android常見內(nèi)存泄漏場景

3.1 內(nèi)部類持有外部類引用造成的內(nèi)存泄漏

1. 非靜態(tài)內(nèi)部類

我們知道非靜態(tài)內(nèi)部類可以訪問外部類的變量,它通過變量this$0隱式地持有外部類的引用型型,這個變量是編譯器為非靜態(tài)內(nèi)部類添加的段审,如果內(nèi)部類的生命周期超過外部類,則會引發(fā)內(nèi)存泄漏闹蒜。

造成這種情況的具體原因很多寺枉,可能是多線程或者監(jiān)聽器未反注冊。如果需要快速修復(fù)绷落,可以將內(nèi)部類改為static姥闪,但是static變量的生命周期與App相同,該變量不會被回收砌烁。因此最好是在出現(xiàn)內(nèi)存泄漏時筐喳,通過引用鏈尋找可以切斷的地方。后文的監(jiān)聽器和Handler都屬于這種情況函喉。

2. 匿名內(nèi)部類

匿名內(nèi)部類引發(fā)內(nèi)存泄漏的原因與非靜態(tài)內(nèi)部類相似避归,匿名內(nèi)部類通過xxx$1.class持有了外部類的引用,如果匿名內(nèi)部類的生命周期超過外部類管呵,在外部類例如Activity銷毀時梳毙,內(nèi)部類依舊持有外部類的引用,就會引發(fā)內(nèi)存泄漏捐下。

如果像下面這樣直接在匿名內(nèi)部類中使用Runnable或者Handler時就非常容易引起內(nèi)存泄漏账锹。由于Runnable執(zhí)行的時間很可能超過Activity,Activity在onDestroy()后匿名內(nèi)部類依舊存在坷襟,最終導(dǎo)致Activity泄露奸柬。

button.setOnClickListener(new View.OnClickListener() {
    @override
    public void onClick(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // ......
            }
       }).start();
    }
});

匿名內(nèi)部類引發(fā)的內(nèi)存泄漏不易修改,因為沒有辦法獲得該對象的引用啤握,也就無法在Activity被銷毀時通過引用清除這些資源鸟缕。因此對于可能引發(fā)內(nèi)存泄漏的匿名內(nèi)部類來說,應(yīng)該改為內(nèi)部類實現(xiàn)排抬。

3.2 多線程造成的內(nèi)存泄漏

1. Runnable(Thread)

當(dāng)異步線程持有外部Activity的引用時懂从,如果Activity銷毀時線程還沒有執(zhí)行完,就會導(dǎo)致內(nèi)存泄漏蹲蒲。
解決辦法很簡單番甩,只需要在Activity銷毀之前終止線程即可。

2. AsyncTask

AsyncTaskHandler+Thread的封裝届搁,用于完成異步任務(wù)缘薛。我們在使用時窍育,一般繼承AsyncTask并重寫doInBackground()方法和onPostExecute()方法,doInBackground()方法進(jìn)行耗時操作宴胧,onPostExecute()方法在主線程更新UI漱抓。
其常見的內(nèi)存泄漏原因與Runnable類似,也是由于AsyncTask未執(zhí)行完時Activity被銷毀恕齐,而AsyncTask又持有Activity的引用乞娄,導(dǎo)致Activity無法釋放,引起內(nèi)存泄漏显歧。

對于AsyncTask造成的內(nèi)存泄漏仪或,推薦使用cancel+isCancelled來解決。
如果一個任務(wù)沒有被執(zhí)行并且cancel方法被調(diào)用士骤,那么任務(wù)會立即取消且不會被執(zhí)行范删。對于已經(jīng)在執(zhí)行的任務(wù),cancel方法只能保證其onPostExecute()不會被執(zhí)行拷肌,也就是說到旦,即使調(diào)用了cancel方法,任務(wù)也不會立即停止廓块,需要等待doInBackground()方法完成厢绝。cancel方法不會終止一個正在運(yùn)行的線程,只是給它設(shè)置cancelled狀態(tài)带猴,通知該線程應(yīng)該中斷了昔汉。
因此給任務(wù)調(diào)用cancel方法后還要檢查當(dāng)前task的狀態(tài),保證其及時退出拴清。

@Override
protected Integer doInBackground(Void... args) {
    // Task被取消了靶病,馬上退出
    if(isCancelled()) return null;
    .......
    // Task被取消了,馬上退出
    if(isCancelled()) return null;
}

雖然有這樣的解決辦法口予,但是對于異步操作娄周,這里更推薦RxJava。

3.3 視圖造成的內(nèi)存泄漏

1. WebView

在進(jìn)行混合開發(fā)時沪停,經(jīng)常需要在Activity中嵌入WebView來訪問前端頁面煤辨,此時需要注意WebView的創(chuàng)建和回收問題。
在Activity中使用WebView時木张,推薦使用動態(tài)創(chuàng)建和回收的方式進(jìn)行管理众辨。在布局文件中定義一個ViewGroup,然后動態(tài)地將WebView添加到ViewGroup中舷礼。

@override
protected void onCreate(Bundle savedInstanceState) {
    mWebView = new WebView(this);
    // WebView settings
    mWebView.setWebViewClient(...);
    mWebView.setWebChromeClient(...);
    // 將 WebView 添加到布局中的 ViewGroup 中
    FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    mWebViewLayout.addView(mWebView, layoutParams);
}

之后在Activity的onDestroy()方法中回收WebView相關(guān)資源鹃彻。由于WebView內(nèi)部存在component callbacks,該回調(diào)在onAttachedToWindow()方法中進(jìn)行注冊妻献,并在onDetachedFromWindow()方法中進(jìn)行反注冊蛛株。為了順利反注冊該回調(diào)团赁,需要在WebView執(zhí)行destroy()之前將其從布局上移除。(具體見下方的參考2)

@override
protected void onDestroy() {
    // 從父容器移除 WebView 后再將其銷毀
    if (mWebView != null) {
        mWebView.loadDataWithBaseURL(
                null, "", "text/html", "utf-8", "");
        mWebView.clearHistory();
        mWebView.setWebViewClient(null);
        mWebView.setWebChromeClient(null);       
        mWebViewLayout.removeView(mWebView);
        mWebView.destroy();
        mWebView = null;
    }
}
2. static view

如果某個View在初始化時需要消耗大量資源谨履,并且要求其在Activity生命周期中不變欢摄,就可能將其修飾為static加載到視圖樹上。由于View在新建時就持有Activity的引用屉符,因此Activity銷毀時需要釋放資源剧浸。

public View(Context context) {
    mContext = context; // 此時View已經(jīng)持有Activity的引用
    // ......
}

面對這種情況锹引,最好是將View設(shè)置為普通變量矗钟,可以避免這類內(nèi)存泄漏。

3.4 廣播嫌变、監(jiān)聽器等未反注冊

這一類的內(nèi)存泄漏主要與觀察者模式有關(guān)吨艇,一般情況下是有多個觀察者(Observer)對同一個被觀察者(Observable)進(jìn)行監(jiān)聽。
如果有一個Manager對觀察者進(jìn)行統(tǒng)一管理的話腾啥,那么觀察者的對被觀察者監(jiān)聽的注冊反注冊一定是成對出現(xiàn)的东涡,不然就會出現(xiàn)內(nèi)存泄漏谅年。在監(jiān)聽器一節(jié)中會詳細(xì)描述這種場景霍弹。

1. 廣播

廣播的主要流程如下:

1:廣播接收者BroadcastReceiver通過Binder機(jī)制向AMS(Activity Manager Service)進(jìn)行注冊
2:廣播發(fā)送者通過binder機(jī)制向AMS發(fā)送廣播
3:AMS查找符合相應(yīng)條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發(fā)送到BroadcastReceiver(一般情況下是Activity)相應(yīng)的消息循環(huán)隊列中
4:消息循環(huán)執(zhí)行拿到此廣播丸边,回調(diào)BroadcastReceiver中的onReceive()方法

根據(jù)上述流程凸舵,Activity在銷毀之前應(yīng)及時反注冊祖娘,否則廣播管理者會一直保留當(dāng)前Activity的引用,而廣播管理者的生命周期是整個Application啊奄,最終會導(dǎo)致內(nèi)存泄漏渐苏。

2. 監(jiān)聽器

上面提過,如果存在一個統(tǒng)一的Manager對監(jiān)聽器進(jìn)行管理的話菇夸,注冊和反注冊一定要成對出現(xiàn)琼富,否則很容易出現(xiàn)內(nèi)存泄漏的情況。下面來分析該場景庄新。

假設(shè)當(dāng)前存在一個監(jiān)聽器如下所示鞠眉。

public interface MyListener {
    void run(...);
}

定義一個ListenerManager來對所有的監(jiān)聽器進(jìn)行管理。

public class ListenerManager {
    // 單例模式
    private static final INSTANCE = new ListenerManager();
    // 存儲所有的監(jiān)聽器
    private List<MyListener> mListeners = new CopyOnWriteArrayList<>();

    public static ListenerManager getInstance() {
        return INSTANCE;
    }

    // 注冊監(jiān)聽器時將該監(jiān)聽器添加到列表中
    public void registerListener(MyListener listener) {
        if (listener == null) return;
        if (mListeners.contains(listener)) return;
        mListeners.add(listener);
    }

    // 反注冊時將該監(jiān)聽器從列表中移除
    public void unRegisterListener(MyListener listener) {
        if (listener == null) return false;
        return mListeners.remove(listener);
    }

    public void run() {
        for (MyListener listener : mListeners) {
            listener.run(...);
        }
    }
}

在使用到該監(jiān)聽的Activity中添加如下代碼择诈。

public class TestActivity {
    private TestListener mTestListener;

    @override
    protected void onCreate(...) {
        // ...
        mTestListener = new TestListener();
        ListenerManager.getInstance().registerListener(mTestListener);
    }

    @override
    protected void onDestroy() {
        // ...
        ListenerManager.getInstance().unRegisterListener(mTestListener);
    }

    private class TestListener implements MyListener {
        @override
        void run(...) {
            // ...
        }
    }
}

可以看到械蹋,在Activity中使用了內(nèi)部類的形式定義了監(jiān)聽器,隨后在onCreate()方法中注冊吭从,并在onDestroy()中反注冊朝蜘。那么如果沒有反注冊會出現(xiàn)什么情況呢?

首先ListenerManager的生命周期比Activity要長涩金,如果Activity未進(jìn)行反注冊谱醇,ListenerManager中的mListeners會一直持有TestListener對象的引用暇仲,又因為TestListener是內(nèi)部類,它持有Activity的引用副渴。
最終形成了ListenerManager->mListeners->mTestListener->Activity的引用鏈奈附,導(dǎo)致Activity無法被釋放,形成了內(nèi)存泄漏煮剧。

3.5 其余情況

1. Handler

Handler作為Android的一種消息機(jī)制斥滤,通過HandlerMessage勉盅、MessageQueue佑颇、Looper四個類協(xié)調(diào)合作完成通信任務(wù)。
其中草娜,Message是消息實體挑胸,包含硬件消息和軟件消息;
MessageQueue是消息隊列宰闰,主要的功能是向消息池投遞消息和取走消息池的消息茬贵;
Handler是輔助類,主要功能是向消息池發(fā)送消息事件(Handler.sendMessage())和處理相應(yīng)消息事件(Handler.handleMessage())移袍;
Looper是循環(huán)機(jī)制解藻,不斷循環(huán)執(zhí)行將消息分發(fā)給目標(biāo)處理者。

如果我們在Activity中創(chuàng)建非靜態(tài)的Handler實例并重寫handleMessage()方法葡盗,此時Handler隱式持有外部Activity的引用螟左,而MessageQueue會持有Message引用,Message又持有Handler引用(Message需要知道自己會被發(fā)往哪個Handler)戳粒。
也就是說路狮,如果Message不被消費(fèi),Activity就不會被釋放蔚约,如果使用postDelayed奄妨,在信息被消費(fèi)前關(guān)閉了Activity,就會造成內(nèi)存泄漏苹祟。

面對這種情況砸抛,最好是在Activity執(zhí)行onDestroy()時調(diào)用HandlerremoveCallbacksAndMessages清除所有信息;也可以選擇將Handler定義為靜態(tài)內(nèi)部類树枫,這樣就不會持有外部Activity的引用了直焙。

2. 資源未關(guān)閉

資源性對象(比如Cursor、File等)往往都做了一些緩沖砂轻,應(yīng)該在Activity銷毀時及時關(guān)閉或者注銷奔誓,否則這些資源將不會被回收,造成內(nèi)存泄漏搔涝。

3. 工具類生命周期問題

有時代碼中會新建工具類用于完成一系列相同的操作厨喂,某些工具類在新建時需要傳入Context和措,如下所示。

public class Utils {
    private Context mContext;
    public Utils(Context context) {
        mContext = context;
    }
}

有時候工具類對象是在Activity內(nèi)部新建的蜕煌,它的生命周期與Activity的生命周期相同派阱,那么即使它持有context也不會引發(fā)內(nèi)存泄漏問題。但是如果工具類的生命周期比Activity長(如單例)斜纪,那么傳入了哪個Activity的context贫母,哪個Activity就會泄露。
正確的做法是使用ApplicationContext代替Context盒刚,使得工具類的生命周期與APP相同腺劣,就不會引發(fā)Activity的內(nèi)存泄露。

不過如果該工具類只在某幾個場景下用到呢伪冰?如果它的生命周期還是整個APP誓酒,雖然沒有內(nèi)存泄漏,但也是浪費(fèi)了一部分內(nèi)存贮聂。這時候就需要開發(fā)人員對工具類的生命周期進(jìn)行管理,可以選擇在合適的時候清除該工具類對象寨辩。

四吓懈、參考

  1. 公司大佬筆記
  2. WebView內(nèi)存泄漏--解決方法小結(jié)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市靡狞,隨后出現(xiàn)的幾起案子耻警,更是在濱河造成了極大的恐慌,老刑警劉巖甸怕,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甘穿,死亡現(xiàn)場離奇詭異,居然都是意外死亡梢杭,警方通過查閱死者的電腦和手機(jī)温兼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來武契,“玉大人募判,你說我怎么就攤上這事≈渌簦” “怎么了届垫?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長全释。 經(jīng)常有香客問我装处,道長,這世上最難降的妖魔是什么浸船? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任妄迁,我火速辦了婚禮找前,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘判族。我一直安慰自己躺盛,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布形帮。 她就那樣靜靜地躺著槽惫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辩撑。 梳的紋絲不亂的頭發(fā)上界斜,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機(jī)與錄音合冀,去河邊找鬼各薇。 笑死,一個胖子當(dāng)著我的面吹牛君躺,可吹牛的內(nèi)容都是我干的峭判。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼棕叫,長吁一口氣:“原來是場噩夢啊……” “哼林螃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起俺泣,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤疗认,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伏钠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體横漏,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年熟掂,在試婚紗的時候發(fā)現(xiàn)自己被綠了缎浇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡打掘,死狀恐怖华畏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尊蚁,我是刑警寧澤亡笑,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站横朋,受9級特大地震影響仑乌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一晰甚、第九天 我趴在偏房一處隱蔽的房頂上張望衙传。 院中可真熱鬧,春花似錦厕九、人聲如沸蓖捶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俊鱼。三九已至,卻和暖如春畅买,著一層夾襖步出監(jiān)牢的瞬間并闲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工谷羞, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留帝火,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓湃缎,卻偏偏與公主長得像犀填,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子雁歌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一.什么是內(nèi)存泄漏 在Java程序中宏浩,如果一個對象沒有利用價值了,正常情況下gc是會對其進(jìn)行回收的靠瞎,但是此時仍然有...
    l_genius閱讀 1,509評論 0 3
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏...
    _痞子閱讀 1,633評論 0 8
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題求妹。內(nèi)存泄漏...
    神奇的小蘑菇閱讀 530評論 0 0
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題乏盐。內(nèi)存泄漏...
    apkcore閱讀 1,221評論 2 7
  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏大家都不陌生了制恍,簡單粗俗的講父能,...
    DreamFish閱讀 791評論 0 5