Memory leak with Timer

2019-07-07 04:19发布

问题:

I am using a timer that is canceled and restarted on a listener event. It all works fine except that the the timer thread leaks the whole outer class.

My timer implementation is as follows:

        if(timer != null) {
            timer.cancel();
            timer = null;
            timer = new Timer();
        }
        timer.schedule(new TimerTask() { // Thread leaks!!!!
            @Override
            public void run() {
              mCallback.onHeaderMoving(newToolbarTranslationY );
            }
        } , 150);

I used MAT Analyser to track down the problem and ended up there. I also commented out the line with the callback but the thread still leaks so it is defenetly the timer itself. However I don't really understand what is the problem with that code.

As far as I understand from my research the problem is that the anonymous inner class (new Timertask()) holds a reference to the outer class and therefore can leak the whole context. But I still don't understand why the timer and also the reference to the context is not garbage collected after the thread runs out (after 150 ms +).

Is the context in this case somehow still not released even after the thread finished?

And finally how do I solve this leak? I set the timer to null but that didn't solved my problem.

Edit

 private OnHeaderMovingCallBack mCallback;
 private Timer timer = new Timer();

 //... some other parameters


public ScrollingToolbarManager(View toolbar , View pagerStrip , AbsListView listView , OnHeaderMovingCallBack headerMovingCallBack){
    this.toolbar = toolbar;
    this.pagerStrip = pagerStrip;

    this.listView = listView;

    mCallback = headerMovingCallBack;

    changeStartValues();

}

public static interface OnHeaderMovingCallBack{
    public void onHeaderMoving(int translationY);
}

 public void moveHeader(){

      //... some calculations

    //timer implementation from above
 }

moveHeader() is called on a scroll event of a listview

回答1:

If you think that the problem is that the anonymous inner class holds a reference to the outer class, then simply use a static named inner class - this will hold no reference. Put something like this inside your class:

static class MyTimerTask extends TimerTask {
    private OnHeaderMovingCallBack mCallback;
    int newToolbarTranslationY;

    public MyTimerTask(OnHeaderMovingCallBack mCallback, int newToolbarTranslationY) {
        this.mCallback = mCallback;
        this.newToolbarTranslationY = newToolbarTranslationY;
    }

    @Override
    public void run() {
        mCallback.onHeaderMoving(newToolbarTranslationY);
    }
}


回答2:

I have the same problem with you. I found that when I define Timer as global var and don't set it to null when the activity finished, it always leads memory leak. And when I define Timer as local var or set it to null, the problem gone.But I don't understand why. If you had solved it, please tell me your solution, thanks!

public class TestActivity extends AppCompatActivity {
    private Timer mTimer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 0);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTimer = null;
    }
}