当收集子类的最后一个实例时,是否收集抽象超类垃圾?
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);
}
}
很明显,静态代码块执行一次,并定期调度timer
。 timerTask
查看队列并调用 onExpire()
。
怎么了?
这个 运行 暂时没问题,但突然 timerTask
不再执行了。测试的时候还可以,我模拟不了,生产一段时间后就不行了。
我不确定会发生什么,但我怀疑我在静态代码块中初始化的静态变量在收集 subclass 的最后一个实例时被垃圾收集。然后,当class被重新使用时,静态代码块不再是运行。换句话说,它似乎一直有效,直到不再有 extend ExpirableCheap
.
的实例为止
奇怪的是,queue
仍然存在,这就是我预计 Runnable
内部会发生异常的原因,但我认为情况并非如此。
如您所见,我尝试将 timer
和 timerTask
变量从静态代码块移动到成员变量中(这没有帮助)。我还尝试同步 setExpire()
和 resetExpire()
,我相信这也没什么区别。
有人能看到发生了什么吗?我是不是又犯了一个愚蠢的错误,是不是走错了路?
有什么建议我可以更改以完成这项工作吗?
正如@TimBiegeleisen 正确指出的那样,Java 按预期工作。当收集子类的最后一个实例时,抽象超类不会被垃圾收集。
我的问题与此无关。
我有一个摘要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);
}
}
很明显,静态代码块执行一次,并定期调度timer
。 timerTask
查看队列并调用 onExpire()
。
怎么了?
这个 运行 暂时没问题,但突然 timerTask
不再执行了。测试的时候还可以,我模拟不了,生产一段时间后就不行了。
我不确定会发生什么,但我怀疑我在静态代码块中初始化的静态变量在收集 subclass 的最后一个实例时被垃圾收集。然后,当class被重新使用时,静态代码块不再是运行。换句话说,它似乎一直有效,直到不再有 extend ExpirableCheap
.
奇怪的是,queue
仍然存在,这就是我预计 Runnable
内部会发生异常的原因,但我认为情况并非如此。
如您所见,我尝试将 timer
和 timerTask
变量从静态代码块移动到成员变量中(这没有帮助)。我还尝试同步 setExpire()
和 resetExpire()
,我相信这也没什么区别。
有人能看到发生了什么吗?我是不是又犯了一个愚蠢的错误,是不是走错了路?
有什么建议我可以更改以完成这项工作吗?
正如@TimBiegeleisen 正确指出的那样,Java 按预期工作。当收集子类的最后一个实例时,抽象超类不会被垃圾收集。
我的问题与此无关。