Android記憶體最佳化11 記憶體流失常見情況2 線程持久化

來源:互聯網
上載者:User

標籤:bsp   cte   退出   聲明   答案   task   應該   mes   執行   

線程持久化

Java中的Thread有一個特點就是她們都是直接被GC Root所引用,也就是說Dalvik虛擬機器對所有被啟用狀態的線程都是持有強引用,導致GC永遠都無法回收掉這些線程對象,除非線程被手動停止共置為null或者使用者直接kill進程操作。所以當使用線程時,一定要考慮在Activity退出時,及時將線程也停止並釋放掉

記憶體流失1:AsyncTask
void startAsyncTask() {    new AsyncTask<Void, Void, Void>() {        @Override protected Void doInBackground(Void... params) {            while(true);        }    }.execute();}super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);View aicButton = findViewById(R.id.at_button);aicButton.setOnClickListener(new View.OnClickListener() {    @Override public void onClick(View v) {        startAsyncTask();        nextActivity();    }});

 

使用LeakCanary檢測到的記憶體流失:

為什嗎?
上面代碼在activity中建立了一個匿名類AsyncTask,匿名類和非靜態內部類相同,會持有外部類對象,這裡也就是activity,因此如果你在Activity裡聲明且執行個體化一個匿名的AsyncTask對象,則可能會發生記憶體流失,如果這個線程在Activity銷毀後還一直在後台執行,那這個線程會繼續持有這個Activity的引用從而不會被GC回收,直到線程執行完成。

怎麼解決?
自訂靜態AsyncTask類,並且讓AsyncTask的周期和Activity周期保持一致,也就是在Activity生命週期結束時要將AsyncTask cancel掉。

記憶體流失2:Handler

代碼如下:
MainActivity.java

...void createHandler() {    new Handler() {        @Override public void handleMessage(Message message) {            super.handleMessage(message);        }    }.postDelayed(new Runnable() {        @Override public void run() {            while(true);        }    }, 1000);}...View hButton = findViewById(R.id.h_button);hButton.setOnClickListener(new View.OnClickListener() {    @Override public void onClick(View v) {        createHandler();        nextActivity();    }});...

 

為什嗎?

建立的Handler對象為匿名類,匿名類預設持有外部類activity, Handler通過發送Message與主線程互動,Message發出之後是儲存在MessageQueue中的,有些Message也不是馬上就被處理的。這時activity被handler持有
handler被message持有,message被messagequeue持有,message queue被loop持有,主線程的loop是全域存在的,這時就造成activity被臨時性持久化,造成臨時性記憶體流失

怎麼解決?
可以由上面的結論看出,產生泄漏的根源在於匿名類持有Activity的引用,因此可以自訂Handler和Runnable類並聲明成靜態內部類,來解除和Activity的引用。或者在activity 結束時,將發送的Message移除

記憶體流失3:Thread

代碼如下:
MainActivity.java

void spawnThread() {    new Thread() {        @Override public void run() {            while(true);        }    }.start();}View tButton = findViewById(R.id.t_button);tButton.setOnClickListener(new View.OnClickListener() {  @Override public void onClick(View v) {      spawnThread();      nextActivity();  }});

 

為什嗎?
Java中的Thread有一個特點就是她們都是直接被GC Root所引用,也就是說Dalvik虛擬機器對所有被啟用狀態的線程都是持有強引用,導致GC永遠都無法回收掉這些線程對象,除非線程被手動停止共置為null或者使用者直接kill進程操作。看到這相信你應該也是心中有答案了吧 : 我在每一個MainActivity中都建立了一個線程,此線程會持有MainActivity的引用,即使退出Activity當前線程因為是直接被GC Root引用所以不會被回收掉,導致MainActivity也無法被GC回收

怎麼解決?
當使用線程時,一定要考慮在Activity退出時,及時將線程也停止並釋放掉

記憶體流失4:Timer Tasks

代碼如下:
MainActivity.java

void scheduleTimer() {    new Timer().schedule(new TimerTask() {        @Override        public void run() {            while(true);        }    },1000);}View ttButton = findViewById(R.id.tt_button);ttButton.setOnClickListener(new View.OnClickListener() {    @Override public void onClick(View v) {        scheduleTimer();        nextActivity();    }});

 

為什嗎?
這裡記憶體流失在於Timer和TimerTask沒有進行Cancel,從而導致Timer和TimerTask一直引用外部類Activity。

怎麼解決?
在適當的時機進行Cancel。

Android記憶體最佳化11 記憶體流失常見情況2 線程持久化

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.