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

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

我有一个摘要class,我用它来使子实例过期class:

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;
        }
    }
}

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

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

这个 class 工作得很好,但是 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)
    );

    @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);
    }
}

很明显,静态代码块执行一次,并定期调度timertimerTask 查看队列并调用 onExpire()

怎么了?

这个 运行 暂时没问题,但突然 timerTask 不再执行了。测试的时候还可以,我模拟不了,生产一段时间后就不行了。

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

的实例为止

奇怪的是,queue 仍然存在,这就是我预计 Runnable 内部会发生异常的原因,但我认为情况并非如此。

如您所见,我尝试将 timertimerTask 变量从静态代码块移动到成员变量中(这没有帮助)。我还尝试同步 setExpire()resetExpire(),我相信这也没什么区别。

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

有什么建议我可以更改以完成这项工作吗?

正如@TimBiegeleisen 正确指出的那样,Java 按预期工作。当收集子类的最后一个实例时,抽象超类不会被垃圾收集。

我的问题与此无关。