Собирается ли мусор абстрактного суперкласса, когда собирается последний экземпляр подкласса? - PullRequest
0 голосов
/ 18 октября 2018

У меня есть абстрактный класс, который я использую для истечения срока действия экземпляров подкласса:

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

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

Очевидно, что статический кодовый блок выполняется один раз и планирует timer через регулярные интервалы.timerTask заглядывает в очередь и вызывает onExpire().

Что не так?

Некоторое время это работает нормально, но затем внезапно timerTask больше не выполняется.При тестировании он работает нормально, и я не могу смоделировать ситуацию, но после некоторого времени в работе это не удается.

Я не уверен, что произойдет, но я подозреваю, что статические переменные, которые я инициализировал в блоке статического кода,сборка мусора при сборке последнего экземпляра подкласса.Затем, когда класс используется повторно, блок статического кода больше не запускается.Другими словами, кажется, что это работает до тех пор, пока не останется ни одного экземпляра, который extend ExpirableCheap.

Странно, что queue сохраняется, причина, по которой я ожидал, что исключение произойдет внутри Runnable, что, я считаю,дело не в этом.

Как видите, я пытался переместить переменные timer и timerTask из блока статического кода в переменные-члены (что не помогло).Я также пытался синхронизировать setExpire() и resetExpire(), что, как мне кажется, также не имеет значения.

Кто-нибудь может увидеть, что происходит?Я совершил еще одну глупую ошибку, и я на неправильном пути?

Есть предложения, что я мог бы изменить, чтобы это сработало?

1 Ответ

0 голосов
/ 23 октября 2018

Как правильно заметил @TimBiegeleisen, Java работает должным образом.Абстрактный суперкласс НЕ является сборщиком мусора при сборке последнего экземпляра подкласса.

Моя проблема не связана.

...