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

我有一个抽象类,用于使子类的实例过期:


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;

        }

    }

}

我扩展任何类并覆盖onExpire,然后我setExpire(delay)从子类(未显示)调用:


public class MyClass extends Expirable {

    @Override

    protected void onExpire() {

        // expiration code here

    }

}

这个类工作得很好,但timer对象非常昂贵。因为我有数万个实例,所以我写了一个具有相同功能的廉价版本,它使用timer一个固定速率的调度和一个queue. 不那么精确,但便宜。


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)

    );


显然,静态代码块执行一次并定期调度timer。将timerTask在队列和呼叫偷窥onExpire()。


怎么了?


这运行了一段时间,但突然timerTask不再执行。测试时,它工作正常,我无法模拟这种情况,但在生产一段时间后它失败了。


我不确定会发生什么,但我怀疑我在静态代码块中初始化的静态变量在收集子类的最后一个实例时被垃圾收集。然后,当类被重用时,静态代码块不会再次运行。换句话说,它似乎一直有效,直到不再有 which extend ExpirableCheap.


奇怪的是queue,为什么我期望在 内部发生异常的原因仍然存在,Runnable我认为情况并非如此。


如您所见,我尝试将静态代码块中的变量timer和timerTask变量移动到成员变量中(这没有帮助)。我也尝试同步setExpire()and resetExpire(),我相信这也没有区别。


有人能看到发生了什么吗?我是否又犯了另一个愚蠢的错误并且我走错了路?


有什么建议我可以改变以使其工作吗?


天涯尽头无女友
浏览 104回答 1
1回答

波斯汪

正如@TimBiegeleisen 正确指出的那样,Java 按预期工作。当收集子类的最后一个实例时,抽象超类不会被垃圾收集。我的问题是无关的。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java