在android開發過程當中,咱們可能會遇到過使人奔潰的OOM異常,面對這樣的異常咱們是既熟悉又深惡痛絕的,由於形成OOM的緣由有不少種狀況,如加載圖片過大,某已再也不使用的類未被GC及時回收等等……本篇咱們就來分析其中一種形成OOM的場景,它就是罪惡的內存泄漏。對於這樣的稱呼,咱們並不陌生,甚至多次與之"並肩做戰",只不過它就是一個豬隊友,只會不斷送塔…….java
本篇分爲3部分:android
1.Handler內存泄漏例子說明以及原理闡明
2.問題驗證(若是感受繁瑣請直接跳過)
3.Handler內存泄漏解決方法
eg.Handler內存泄漏例子說明以及原理闡明web
Handler,咱們已經至關熟悉了,並且常常用得不亦樂乎,但就是由於太熟悉了,纔會偶爾被它反捅一刀,血流不止……還記得咱們曾經滿懷信心地使用着以下的優美而又簡潔的代碼不?數組
不怕你嚇着,實話告訴你,這個代碼已經形成內存泄漏了!!!不相信?咱們使用Android lint工具檢測一下該類的代碼:app
面對現實吧,那爲何會這樣呢?在java中非靜態內部類和匿名內部類都會隱式持有當前類的外部引用,因爲Handler是非靜態內部類因此其持有當前Activity的隱式引用,若是Handler沒有被釋放,其所持有的外部引用也就是Activity也不可能被釋放,當一個對象一句不須要再使用了,原本該被回收時,而有另一個正在使用的對象持有它的引用從而致使它不能被回收,這致使本該被回收的對象不能被回收而停留在堆內存中,這就產生了內存泄漏(上面的例子就是這個緣由)。最終也就形成了OOM
…….咱們再來段清晰的代碼,咱們來使用mHandler發送一個延遲消息:dom
分析:當咱們執行了HandlerActivity的界面時,被延遲的消息會在被處理以前存在於主線程消息隊列中5分鐘,而這個消息中又包含了Handler的引用,而咱們建立的Handler又是一個匿名內部類的實例,其持有外部HandlerActivity的引用,這將致使了HandlerActivity沒法回收,進行致使HandlerActivity持有的不少資源都沒法回收,從而就形成了傳說中的內存泄露問題!ide
爲了進一步驗證內存泄漏問題,咱們在該類中建立一個int數組,該數組分配的內存大小爲2m,而後咱們用DDMS來查看heap內存,而後使用GC回收,看看內存會不會有變化:工具
第一次啓動app時,head內存爲12.5M,已經分配內容(Allocated):8.5M,空閒內存:4M,咱們頻繁點擊GC按鈕,內存並無發生明顯變化,如今咱們點擊手機返回健,推出應用,而後再從新進入,一樣檢測一下head內存:post
咱們發現head內存:20.5M,Allocated:16.5M,Free:4M,堆內存和已經分配內存近乎翻倍,咱們繼續頻繁點擊GC, 看看可否被回收?結果內存並無明顯變化,如今咱們繼續點擊手機返回健,推出應用,而後再從新進入,一樣再次檢測一下head內存:this
咱們發現head內存:28.5M,Allocated:24.5M,Free:4M,堆內存和已經分配內存又增長了,並且不管咱們如何點擊GC回收內存,內存都沒有明顯變化,並且每啓動一次該頁面,內存就增長一倍!這也就說存在在某個類只建立而沒銷燬的狀況,其實就是存在內存泄漏問題。咱們使用MAT工具進一步驗證這個問題,咱們來看一組Histogram的數據和dominator tree數據,首先是Histogram的數據:
dominator tree數據:
同時存在三個同樣的HandlerActivity和內部類,這就足以說明HandlerActvity只有建立沒被銷燬了吧,也就是說Handler形成的內存泄漏真的存在。
解決這個問題思路就是使用靜態內部類並繼承Handler時(或者也能夠單獨存放成一個類文件)。由於靜態的內部類不會持有外部類的引用,因此不會致使外部類實例的內存泄露。當你須要在靜態內部類中調用外部的Activity時,咱們可使用弱引用來處理。另外關於一樣也須要將Runnable設置爲靜態的成員屬性。修改後不會致使內存泄露的代碼以下:
方法:聲明靜態匿名內部類+弱引用
public class HandlerActivity extends Activity {
//建立一個2M大小的int數組
int[] datas=new int[1024*1024*2];
// Handler mHandler = new Handler(){
// @Override
// public void handleMessage(Message msg) {
// super.handleMessage(msg);
// }
// };
/**
* 建立靜態內部類
*/
private static class MyHandler extends Handler{
//持有弱引用HandlerActivity,GC回收時會被回收掉.
private final WeakReference<HandlerActivity> mActivty;
public MyHandler(HandlerActivity activity){
mActivty =new WeakReference<HandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity=mActivty.get();
super.handleMessage(msg);
if(activity!=null){
//執行業務邏輯
}
}
}
private static final Runnable myRunnable = new Runnable() {
@Override
public void run() {
//執行咱們的業務邏輯
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_leak);
MyHandler myHandler=new MyHandler(this);
//解決了內存泄漏,延遲5分鐘後發送
myHandler.postDelayed(myRunnable, 1000 * 60 * 5);
}
}
複製代碼