內(nèi)存泄露:Thread是如何造成內(nèi)存泄露的

先來看一段使用Thread的代碼侧漓,簡單而常見

public class MainActivity extends Activity {

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

        // 開啟線程缺前,啟動循環(huán)任務
        new Thread() {
            @Override
            public void run() {
                while(true) {
                    SystemClock.sleep(1000);
                }
            }
        }.start();
    }
}

Ok蛀醉,想一下,如果用戶旋轉了屏幕衅码,這時會發(fā)生什么拯刁?

正常情況下,系統(tǒng)新創(chuàng)建一個橫屏的Activity實例逝段,銷毀舊的Activity實例并釋放相關資源垛玻。

但在這里割捅,舊的Activity會長久的存在,直到發(fā)生OOM夭谤。為什么棺牧?

首先,它內(nèi)部匿名的Thread實例會長久運行朗儒,不會被系統(tǒng)GC回收颊乘。Threads在Java中是GC roots,意味著 Dalvik Virtual Machine (DVM) 會為所有活躍的threads在運行時系統(tǒng)中保持一個硬引用醉锄,這會導致threads一直處于運行狀態(tài)乏悄,垃圾收集器將永遠不可能回收它。

其次恳不,非靜態(tài)內(nèi)部類會持有外部類的引用檩小。Thread會長久地持有Activity的引用,使得系統(tǒng)無法回收Activity和它所關聯(lián)的資源和視圖烟勋。

針對后者规求,MainActivity的內(nèi)存泄漏問題,只需把非靜態(tài)的Thread匿名類定義成靜態(tài)的內(nèi)部類就行了卵惦。

public class MainActivity extends Activity {  
   
  @Override  
  protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    exampleTwo();  
  }  
   
  private void exampleTwo() {  
    new MyThread().start();  
  }  
   
  private static class MyThread extends Thread {  
    @Override  
    public void run() {  
      while(true) {  
        SystemClock.sleep(1000);  
      }  
    }  
  }  
}  

現(xiàn)在新創(chuàng)建的Thread不會持有MainActivity的一個隱式引用阻肿,當手機屏幕旋轉時不會阻止垃圾回收器對舊MainActivity的回收工作。

而在第一個問題中沮尿,一旦有一個新的Activity創(chuàng)建丛塌,那么就有一個Thread永遠得不到清理回收,發(fā)生內(nèi)存泄漏畜疾。

出于這個原因赴邻,我們應當為thread添加一個結束的邏輯,如下代碼所示

public class MainActivity extends Activity {  
  private MyThread mThread;  
   
  @Override  
  protectedvoid onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    exampleThree();  
  }  
   
  privatevoid exampleThree() {  
    mThread = new MyThread();  
    mThread.start();  
  }  
   
  /** 
   * Static inner classes don't hold implicit references to their 
   * enclosing class, so the Activity instance won't be leaked across 
   * configuration changes. 
   */  
  private static class MyThread extends Thread {  
    private boolean mRunning = false;  
   
    @Override  
    public void run() {  
      mRunning = true;  
      while(mRunning) {  
        SystemClock.sleep(1000);  
      }  
    }  
   
    public void close() {  
      mRunning = false;  
    }  
  }  
   
  @Override  
  protectedvoid onDestroy() {  
    super.onDestroy();  
    mThread.close();  
  }  
}  

在上述的代碼中啡捶,當Activity結束銷毀時在onDestroy()方法中結束了新創(chuàng)建的線程姥敛,保證了thread不會發(fā)生泄漏。但是如果你想在手機配置發(fā)生改變時届慈,保持原有的線程而不重新創(chuàng)建的話徒溪,你可以考慮使用fragment來保留原有的線程,以備下一次使用金顿。

結論

  1. 使用靜態(tài)內(nèi)部類/匿名類,不要使用非靜態(tài)內(nèi)部類/匿名類鲤桥。非靜態(tài)內(nèi)部類/匿名類會隱式的持有外部類的引用揍拆,外部類就有可能發(fā)生泄漏。而靜態(tài)內(nèi)部類/匿名類不會隱式的持有外部類引用茶凳,外部類會以正常的方式回收嫂拴,如果你想在靜態(tài)內(nèi)部類/匿名類中使用外部類的屬性或方法時播揪,可以顯式的持有一個弱引用。
    </br>
  2. 不要以為Java永遠會幫你清理回收正在運行的threads筒狠。在上面的代碼中猪狈,我們很容易誤以為當Activity結束銷毀時會幫我們把正在運行的thread也結束回收掉,但事情永遠不是這樣的辩恼!Java threads會一直存在雇庙,只有當線程運行完成或被殺死掉,線程才會被回收灶伊。所以我們應該養(yǎng)成為thread設置退出邏輯條件的習慣疆前。
    </br>
  3. 適當?shù)目紤]下是否應該使用線程。Android應用框架設計了許多的類來簡化執(zhí)行后臺任務聘萨,我們可以使用與Activity生命周期相關聯(lián)的Loaders來執(zhí)行簡短的后臺查詢?nèi)蝿罩窠贰H绻粋€線程不依賴與Activity,我們還可以使用Service來執(zhí)行后臺任務米辐,然后用BroadcastReceiver來向Activity報告結果胸完。另外需要注意的是本文討論的thread同樣使用于AsyncTasks,AsyncTask同樣也是由線程來實現(xiàn)翘贮,只不過使用了Java5.0新增并發(fā)包中的功能赊窥,但同時需要注意的是根據(jù)官方文檔所說,AsyncTask適用于執(zhí)行一些簡短的后臺任務
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末择膝,一起剝皮案震驚了整個濱河市誓琼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肴捉,老刑警劉巖腹侣,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異齿穗,居然都是意外死亡傲隶,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門窃页,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跺株,“玉大人,你說我怎么就攤上這事脖卖∑故。” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵畦木,是天一觀的道長袖扛。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么蛆封? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任唇礁,我火速辦了婚禮,結果婚禮上惨篱,老公的妹妹穿的比我還像新娘盏筐。我一直安慰自己,他們只是感情好砸讳,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布琢融。 她就那樣靜靜地躺著,像睡著了一般绣夺。 火紅的嫁衣襯著肌膚如雪吏奸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天陶耍,我揣著相機與錄音奋蔚,去河邊找鬼。 笑死烈钞,一個胖子當著我的面吹牛泊碑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毯欣,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼馒过,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酗钞?” 一聲冷哼從身側響起腹忽,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎砚作,沒想到半個月后窘奏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡葫录,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年着裹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片米同。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡骇扇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出面粮,到底是詐尸還是另有隱情少孝,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布熬苍,位于F島的核電站韭山,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏冷溃。R本人自食惡果不足惜钱磅,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望似枕。 院中可真熱鬧盖淡,春花似錦、人聲如沸凿歼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽答憔。三九已至味赃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虐拓,已是汗流浹背心俗。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蓉驹,地道東北人城榛。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像态兴,于是被迫代替她去往敵國和親狠持。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

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

  • 一.什么是內(nèi)存泄漏 在Java程序中瞻润,如果一個對象沒有利用價值了喘垂,正常情況下gc是會對其進行回收的,但是此時仍然有...
    l_genius閱讀 1,495評論 0 3
  • 一绍撞、基礎知識 1正勒、什么是內(nèi)存泄露 java中的內(nèi)存泄露是指一個無用對象持續(xù)占有內(nèi)存或無用對象的內(nèi)存得不到及時的釋放...
    LiveMoment閱讀 4,417評論 0 20
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,510評論 25 707
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法楚午,內(nèi)部類的語法昭齐,繼承相關的語法,異常的語法矾柜,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • 朝花夕拾阱驾,那金光閃閃的海貝,靜靜的躺在海灘邊怪蔑,風刮日曬里覆,永遠光彩奪目。 2017年8月31日 多云 星期四 時常...
    just_haha閱讀 320評論 0 4