Android常見內(nèi)存泄漏分析

內(nèi)存泄漏

內(nèi)存泄漏就是分配的內(nèi)存空間沒有及時回收導(dǎo)致的肉微。可使用的內(nèi)存變少敬拓,應(yīng)用變卡老速,最后內(nèi)存溢出后應(yīng)用就會掛掉

內(nèi)存泄漏的檢測

建議閱讀Android內(nèi)存泄漏檢測和定位這篇文章,使用里面的檢測方法可以輕松的驗(yàn)證本文中的內(nèi)存泄漏例子

原因

Android內(nèi)存泄漏大多是因?yàn)锳ctivity沒有被回收導(dǎo)致的畏腕,Activity沒有被回收一般分為兩種情況

  • 全局的static變量持有Activity的強(qiáng)引用
  • 在Activity生命周期外的線程缴川,持有Activity的強(qiáng)引用

引用類型

推薦閱讀Java中四種引用類型,感覺是非常容易理解的

靜態(tài)變量

類中定義了靜態(tài)Activity變量描馅,把當(dāng)前的Activity賦值給靜態(tài)變量把夸,如果Activity生命周期結(jié)束的時候靜態(tài)變量沒有清空,就會導(dǎo)致內(nèi)存泄漏铭污。static變量是貫穿整個應(yīng)用的生命周期的恋日,所以被泄漏的Activity就會一直存在于應(yīng)用的進(jìn)程中,不會被回收嘹狞,同樣的持有Activity(Context)的靜態(tài)變量岂膳,比如View也是一樣的道理

private static Activity sActivity;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        sActivity = this;
        findViewById(R.id.btn_back).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

    }

內(nèi)部類

非靜態(tài)內(nèi)部類 和 匿名類 都會潛在的引用它們所屬的外部類,但是靜態(tài)內(nèi)部類卻不會磅网。

private static Test sTest;
    private static Test2 sTest2;

    class Test { }
    static class Test2 { }
    
    private void test() {
        sTest = new Test();
        sTest2=new Test2();
    }
屏幕快照 2019-02-13 下午1.24.04.png-110.3kB

1.如果這個非靜態(tài)內(nèi)部類實(shí)例內(nèi)部做了一些耗時的操作谈截,就會導(dǎo)致外圍對象不會被回收,從而導(dǎo)致內(nèi)存泄漏

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        test();
    }
private void test() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

這個匿名內(nèi)部類會引用這個Activity涧偷,內(nèi)部開了線程做耗時操作簸喂,就會導(dǎo)致這個Activity不能被回收

2.結(jié)合上面的靜態(tài)變量,如果靜態(tài)變量持有非靜態(tài)內(nèi)部類的引用嫂丙,而
非靜態(tài)內(nèi)部類引用了該Activity娘赴,那就會導(dǎo)致這個Activity不能被回收

private static Test sTest;
    class Test {}
    private void test() {
        sTest = new Test();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        test();
    }

Test是非靜態(tài)內(nèi)部類,sTest是靜態(tài)變量跟啤,注意這個靜態(tài)變量是在onCreate中初始化的诽表,會持有該Activity的引用,Activity被銷毀的時候sTest不置空那么該Activity就無法被回收隅肥;

內(nèi)部類常見情況

上面都說了竿奏,非靜態(tài)內(nèi)部類會引用所屬外部類,這時候如果創(chuàng)建一個內(nèi)部類腥放,而且持有一個靜態(tài)變量的引用就容易會引起外部類沒法被回收泛啸;同樣的如果該內(nèi)部類在子線程做了一些耗時操作,屬于在Activity生命周期外的線程秃症,也會導(dǎo)致外部類沒法被回收候址;常見的情況有下面幾種

Threads

上面第一個例子已經(jīng)寫過了

TimerTask

匿名內(nèi)部類嘛吕粹,肯定就持有所在Activity的引用,又做耗時操作岗仑,肯定內(nèi)存泄漏

Handler

一個道理匹耕,匿名內(nèi)部類嘛,肯定就持有所在Activity的引用荠雕,如果執(zhí)行postDelayed的時候稳其,Activity被銷毀,那么Handler持有的Activity沒法被回收炸卑,就內(nèi)存泄漏了既鞠,而且里面也有一個匿名內(nèi)部類Runnable持有Activity

new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        }.postDelayed(new Runnable() {
            @Override
            public void run() {
                
            }
        },10000);

系統(tǒng)服務(wù)

這個也很好理解,當(dāng)你使用系統(tǒng)服務(wù)的時候盖文,可以注冊監(jiān)聽器嘱蛋,會導(dǎo)致服務(wù)持有Context的引用,如果在Activity銷毀的時候椅寺,沒有注銷掉監(jiān)聽器浑槽,就會導(dǎo)致內(nèi)存泄漏;

//傳感器的監(jiān)聽器注冊
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);

注冊廣播接收器也是需要在onDestroy方法里面注銷

資源未釋放

這個簡單返帕,一般就各種流桐玻、各種資源沒有關(guān)閉,集合中對象沒清理導(dǎo)致荆萤,倒是不容易犯錯

靜態(tài)內(nèi)部類解決內(nèi)存泄漏

1.靜態(tài)內(nèi)部類不會持有外部類的引用镊靴,所以使用靜態(tài)內(nèi)部類可以解決以上問題,如果靜態(tài)內(nèi)部類里面需要引用外部類链韭,可以通過弱引用的方式來引用偏竟;
2.用static的變量引用匿名內(nèi)部類的實(shí)例或?qū)⒛涿麅?nèi)部類的實(shí)例化操作放到外部類的靜態(tài)方法中

//靜態(tài)內(nèi)部類
private static class Myhandler extends Handler {
        private final WeakReference<Activity> mActivity;

        public Myhandler(Activity activity) {
            mActivity = new WeakReference<Activity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Activity activity = mActivity.get();
            // ...
        }
    }
    
private final Myhandler mMyhandler=new Myhandler(this);

//這樣寫不會持有外部類的引用
private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMyhandler.postDelayed(sRunnable, 1000 * 60 * 10);
    }

方法沒有問題,但是為什么上面的寫法sRunnable沒有引用外部類而下面的寫法會引用呢敞峭,會導(dǎo)致內(nèi)存泄漏呢踊谋;我覺得是因?yàn)槌跏蓟奈恢貌煌o態(tài)變量和靜態(tài)類先被初始化了旋讹,所以沒有外部類的引用殖蚕;感覺是這樣的吧

private static Runnable sRunnable;
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sRunnable = new Runnable() {
            @Override
            public void run() { /* ... */ }
        };
    }

參考文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沉迹,隨后出現(xiàn)的幾起案子睦疫,更是在濱河造成了極大的恐慌,老刑警劉巖鞭呕,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛤育,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瓦糕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門底洗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刻坊,你說我怎么就攤上這事枷恕。” “怎么了谭胚?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長未玻。 經(jīng)常有香客問我灾而,道長,這世上最難降的妖魔是什么扳剿? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任旁趟,我火速辦了婚禮,結(jié)果婚禮上庇绽,老公的妹妹穿的比我還像新娘锡搜。我一直安慰自己,他們只是感情好瞧掺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布耕餐。 她就那樣靜靜地躺著,像睡著了一般辟狈。 火紅的嫁衣襯著肌膚如雪肠缔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天哼转,我揣著相機(jī)與錄音明未,去河邊找鬼。 笑死壹蔓,一個胖子當(dāng)著我的面吹牛趟妥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佣蓉,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼披摄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了偏螺?” 一聲冷哼從身側(cè)響起行疏,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎套像,沒想到半個月后酿联,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年贞让,在試婚紗的時候發(fā)現(xiàn)自己被綠了周崭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡喳张,死狀恐怖续镇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情销部,我是刑警寧澤摸航,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站舅桩,受9級特大地震影響酱虎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜擂涛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一读串、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撒妈,春花似錦恢暖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挟阻,卻和暖如春琼娘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背附鸽。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工脱拼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坷备。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓熄浓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親省撑。 傳聞我的和親對象是個殘疾皇子赌蔑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

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

  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏大家都不陌生了竟秫,簡單粗俗的講娃惯,...
    DreamFish閱讀 791評論 0 5
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏...
    _痞子閱讀 1,637評論 0 8
  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題肥败。內(nèi)存泄漏大家都不陌生了趾浅,簡單粗俗的講愕提,...
    宇宙只有巴掌大閱讀 2,363評論 0 12
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏...
    apkcore閱讀 1,221評論 2 7
  • 一.Java內(nèi)存分配結(jié)構(gòu)復(fù)習(xí) 1.Java內(nèi)存分配策略 上一篇Android內(nèi)存管理分析總結(jié)中我們提到了Java內(nèi)...
    Geeks_Liu閱讀 820評論 5 7