简体   繁体   English

在收集子类的最后一个实例时是否会收集抽象超类垃圾?

[英]Is an abstract superclass garbage collected when the last instance of the subclass is collected?

I have an abstract class which I use to expire instances of a subclass: 我有一个抽象类,用于终止子类的实例:

public abstract class Expirable {
    private transient Timer timer;

    protected abstract void onExpire();

    protected void setExpire(long delay) {
        resetExpire();

        timer = new Timer();
        timer.schedule(new TimerTask() {
                           @Override
                           public void run() {
                               resetExpire();
                               onExpire();
                           }
                       }, delay
        );
    }

    protected void resetExpire() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }
}

I extend any class and overide onExpire , then I call setExpire(delay) from the subclass (not shown): 我扩展任何类并覆盖onExpire ,然后从子类(未显示)调用setExpire(delay) ):

public class MyClass extends Expirable {
    @Override
    protected void onExpire() {
        // expiration code here
    }
}

This class works perfectly fine, but the timer object is very expensive. 此类工作得很好,但是timer对象非常昂贵。 Because I have tens of thousands of instances, I wrote a cheap version with the same functionality, which uses a single timer scheduled at a fixed rate and a queue . 因为我有成千上万的实例,所以我写了一个便宜的版本,它具有相同的功能,它使用以固定速率安排的单个timer和一个queue Not as precise, but cheap. 没有那么精确,但是便宜。

public abstract class ExpirableCheap {
    private static final long COLLECTOR_INTERVAL_MSEC = 1000;
    private static final int INITIAL_CAPACITY = 128;

    private static Queue<ExpirableCheap> queue = new PriorityBlockingQueue<>(
            INITIAL_CAPACITY,
            Comparator.comparingLong(expirable -> expirable.expiresWhen)
    );

    @SuppressWarnings("FieldCanBeLocal")
    private static TimerTask timerTask;
    @SuppressWarnings("FieldCanBeLocal")
    private static Timer timer;
    static {
        timerTask = new TimerTask() {
            @Override
            public void run() {
                // this Runnable stops working
                long time = new Date().getTime();
                while (queue.peek() != null && queue.peek().expiresWhen < time) {
                    queue.poll().onExpire();
                }
            }
        };
        timer = new Timer();
        timer.scheduleAtFixedRate(timerTask,
                COLLECTOR_INTERVAL_MSEC,
                COLLECTOR_INTERVAL_MSEC
        );
    }

    private transient long expiresWhen;

    protected abstract void onExpire();

    protected synchronized void setExpire(long delay) {
        resetExpire();

        long time = new Date().getTime();
        expiresWhen = time + delay;

        queue.offer(this);
    }

    protected synchronized void resetExpire() {
        queue.remove(this);
    }
}

Obviously, the static code block is executed once and schedules the timer in regular intervals. 显然,静态代码块执行一次,并以固定的时间间隔计划timer The timerTask peeks at the queue and calls onExpire() . timerTask窥视队列并调用onExpire()

What's wrong? 怎么了?

This runs fine for a while, but then suddenly the timerTask is no longer executed. 可以运行一会儿,但是突然之间, timerTask不再执行。 When testing, it works fine and I cannot simulate the situation, but it fails after some time in production. 测试时,它可以正常工作,我无法模拟这种情况,但是在生产一段时间后失败。

I am not sure what happens, but I suspect the static variables which I initialized in the static code block are garbage collected when the last instance of the subclass is collected. 我不确定会发生什么,但是我怀疑在收集子类的最后一个实例时,我在静态代码块中初始化的静态变量被垃圾回收了。 Then, when the class is re-used, the static code block is not run again. 然后,当重新使用该类时,将不再运行静态代码块。 In other words, it seems to work until there are no instances anymore which extend ExpirableCheap . 换句话说,它似乎可以工作,直到不再有extend ExpirableCheap实例extend ExpirableCheap

Strangely, the queue persists, reason why I expected an exception to happen inside the Runnable , which I believe is not the case. 奇怪的是, queue仍然存在,这就是为什么我希望Runnable内发生异常的原因,我认为事实并非如此。

As you can see, I tried to move timer and timerTask variables from the static code block into member variables (which didn't help). 如您所见,我试图将timertimerTask变量从静态代码块移到成员变量中(这没有帮助)。 I also tried to synchronize setExpire() and resetExpire() , which I believe makes no difference either. 我还尝试同步setExpire()resetExpire() ,我认为这也没有什么区别。

Can anybody see what's happening? 有人可以看到发生了什么吗? Did I make another stupid mistake and am I on the wrong track? 我是否犯了另一个愚蠢的错误,是否走错了轨道?

Any suggestions what I could change to make this work? 有什么建议我可以更改以使其正常工作吗?

As @TimBiegeleisen correctly pointed out, Java works as expected. 正如@TimBiegeleisen正确指出的那样,Java可以按预期工作。 An abstract superclass is NOT garbage collected when the last instance of the subclass is collected. 在收集子类的最后一个实例时,不会垃圾收集抽象超类。

My problem was unrelated. 我的问题无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM