有时间质疑JAVA System.GC()和System.runFinilizer
public interface SomeAction {
public void doAction();
}
public class SomePublisher {
private List<SomeAction> actions = new ArrayList<SomeAction>();
public void subscribe(SomeSubscriber subscriber) {
actions.add(subscriber.getAction());
}
}
public class SomeSubscriber {
public static int Count;
public SomeSubscriber(SomePublisher publisher) {
publisher.subscribe(this);
}
public SomeAction getAction() {
final SomeSubscriber me = this;
class Action implements SomeAction {
@Override
public void doAction() {
me.doSomething();
}
}
return new Action();
}
@Override
protected void finalize() throws Throwable {
SomeSubscriber.Count++;
}
private void doSomething() {
// TODO: something
}
}
现在我正试图在主要区块内强制使用GC和终结器。
SomePublisher publisher = new SomePublisher();
for (int i = 0; i < 10; i++) {
SomeSubscriber subscriber = new SomeSubscriber(publisher);
subscriber = null;
}
System.gc();
System.runFinalization();
System.out. println("The answer is: " + SomeSubscriber.Count);
由于无法保证调用JAVA GC调用(如javadoc所述,因为无法保证调用JAVA GC调用(如javadoc和When is the finalize() method called in Java?所述,
我的初学者认为它会随机放置SomeSubscriber.Count。 (System.GC和终结器强制至少为'1'。)
相反,它始终为0.
任何人都可以解释这种行为吗?
(另外,静态成员字段是否与类实例无关,并且在代码执行期间永远不会被销毁?)
答案 0 :(得分:2)
您的测试有一个缺陷 - 即使假设调用System.gc()
和System.runFinalization()
实际上会运行GC并完成,您创建的实例也是 垃圾收集的候选者因此,不会最终确定或收集。
你运行这行10次:
SomeSubscriber subscriber = new SomeSubscriber(publisher);
这会调用SomeSubscriber
的构造函数,其中包含:
publisher.subscribe(this);
因此,发布者对象被赋予对当前正在构造的对象的引用。它有什么作用?
actions.add(subscriber.getAction());
好的,所以它调用订阅者的getAction()
方法,并存储结果。 getAction()
做了什么?
public SomeAction getAction() {
final SomeSubscriber me = this;
class Action implements SomeAction {
@Override
public void doAction() {
me.doSomething();
}
}
return new Action();
}
它会创建本地类的实例。该实例包含封闭的SomeSubscriber
对象的实例。实际上,它有两个这样的实例 - me
,以及每个内部类具有的封闭实例的隐式引用。本地课程是内部课程!
因此,当您在publisher
实例中存储操作列表时,还会存储对其中所有订阅者的引用。当您运行System.gc()
和System.runFinalization()
时,publisher
实例仍然有效,因此这些引用仍然有效,因此您SomeSubscriber
个实例中没有一个实际符合条件垃圾收集。
确保您还指定publisher = null
,然后可能您将能够看到正在运行的结束。我还建议将Count
声明为volatile
(而不是将其称为Count
,但count
- 变量应以小写字母开头),因为通常会将终结器设为以不同的方式运行。