Java內存泄漏常見的代碼片段

常見的內存泄漏

內存泄漏一般情況不會有,但是有了不太好找。一般內存泄漏產生的原因主要有以下幾點。
1.開發人員自己創造出來的內存泄漏代碼
2.底層依賴的代碼存在問題。
3.系統中依賴的包導致的問題。

使用底層包內存泄漏

netty中內存泄漏

https://blog.csdn.net/jiangguilong2000/article/details/42297873

日常代碼中內存泄漏

匿名內部類

和前面的非靜態內部類一樣,匿名內部類也會持有外部類實例的引用。

public class AsyncTaskActivity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	...
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startAsyncTask();  
                finish();
            }
        });
    }
    void startAsyncTask() { //啓動異步執行
        new AsyncTask<Void, Void, Void>() {//異步任務在後臺執行耗時任務期間,AsyncTaskActivity被銷燬
            @Override
            protected Void doInBackground(Void... params) {
                while (true) ;  //任務持續不銷燬
            }
        }.execute();  
    }

在註釋1處實例化了一個AsyncTask,當AsyncTask的異步任務在後臺執行耗時任務期間,AsyncTaskActivity 被銷燬了,被AsyncTask持有的AsyncTaskActivity實例不會被垃圾收集器回收,直到異步任務結束。
解決辦法就是自定義一個靜態的AsyncTask

public class AsyncTaskActivity extends AppCompatActivity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startAsyncTask();
                finish();
            }
        });
    }
    void startAsyncTask() {
        new MyAsyncTask().execute();
    }
    //自定義靜態的類處理
    private static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            while (true) ;
        }
    }
}

非靜態內部類

public class SecondActivity {
    private static Object inner;//靜態內部類
    private Button button;

    @Override
    protected void on(Bundle savedInstanceState) {
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createInnerClass(); //隱式創建
                finish();
            }
        });
    }
    void createInnerClass() {
        class InnerClass {
        }
        inner = new InnerClass();//內部類的創建
    }
}

非靜態內部類會持有外部類實例的引用,如果非靜態內部類的實例是靜態的,就會間接的長期維持着外部類的引用,阻止被系統回收。

Handler內存泄漏

注意兩點可能引起內存泄漏的地方

  • 非靜態內部類(第一種情況)
  • 有延遲消息未在onDestory中移除

Handler的Message被存儲在MessageQueue中,有些Message並不能馬上被處理,它們在MessageQueue中存在的時間會很長,這就會導致Handler無法被回收。如果Handler 是非靜態的,則Handler也會導致引用它的Activity或者Service不能被回收。

public class HandlerActivity extends AppCompatActivity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        final Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendMessageDelayed(Message.obtain(), 60000);
                finish();
            }
        });
    }
}

Handler 是非靜態的匿名內部類的實例,它會隱性引用外部類HandlerActivity 。上面的例子就是當我們點擊Button時,HandlerActivity 會finish,但是Handler中的消息還沒有被處理,因此HandlerActivity 無法被回收。
解決方法就是要使用一個靜態的Handler內部類,Handler持有的對象要使用弱引用,並且在Activity的Destroy方法中移除MessageQueue中的消息,如下所示。

public class HandlerActivity extends AppCompatActivity {
    private Button button;
    private MyHandler myHandler = new MyHandler(this);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myHandler.sendMessageDelayed(Message.obtain(), 60000);
                finish();
            }
        });
    }
    public void show() {
    }
    //靜態的handler
    private static class MyHandler extends Handler {
        private final WeakReference<HandlerActivity> mActivity;
        public MyHandler(HandlerActivity activity) {
            mActivity = new WeakReference<HandlerActivity2>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            if (mActivity != null && mActivity.get() == null) {
                mActivity.get().show();
            }
        }
    }
    @Override
    public void onDestroy() {
        if (myHandler != null) {
            myHandler.removeCallbacksAndMessages(null);
        }
        super.onDestroy();
    }
}

MyHandler是一個靜態的內部類,它持有的 HandlerActivity對象使用了弱引用,並且在onDestroy方法中將Callbacks和Messages全部清除掉。

集合中對象沒清理

,如果我們僅僅釋放引用本身,那麼Vector仍然引用該對象,所以這個對象對GC來說是不可回收的。因此,如果對象加入到Vector後,還必須從Vector中刪除,最簡單的方法就是將Vector對象設置爲null。

Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
    Object o=new Object();
    v.add(o);
    o=null; 
}

所有的Object對象都沒有被釋放,因爲變量v引用這些對象。
還有當集合裏面的對象屬性被修改後,再調用remove()方法時不起作用。

File等資源未關閉

File等,往往都用了緩衝,不使用的時候應該關閉它們。把他們的引用置爲null,而不關閉它們,往往會造成內存泄漏。

監聽器未關閉

單例模式

不正確使用單例模式是引起內存泄漏的一個常見問題,單例對象在初始化後將在JVM的整個生命週期中存在(以靜態變量的方式),如果單例對象持有外部的引用,那麼這個對象將不能被JVM正常回收,導致內存泄漏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章