【问题标题】:java finalizer and gcjava终结器和gc
【发布时间】:2017-02-11 05:28:25
【问题描述】:

是时候质疑 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 和 Finalizer。

 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。 (至少 '1' 作为 System.GC 和终结器的强制。)

相反,它始终为 0。

谁能解释这种行为?

(另外,静态成员字段是否独立于类实例而存在,并且在代码执行期间永远不会被破坏?)

【问题讨论】:

  • 如果你不知道什么是静态字段,你不应该担心终结器或 gc。
  • 静态字段是类变量,不实例化类实例共享的类而存在。
  • “不保证被调用”的哪一部分你没看懂?
  • 你不应该明确地调用System.gc()。在最好的情况下,它什么也不做,在最坏的情况下,它总是会导致完全停止世界的 GC(例如,当 CMS 收集器正在使用时)。
  • 如果您看到来自stackoverflow.com/questions/2506488/… 的 techloris_109 示例,则会手动调用 System.GC() 并在将实例清空后完成块运行。为什么我的情况没有发生同样的事情?

标签: java garbage-collection finalizer


【解决方案1】:

您的测试存在缺陷 - 即使假设调用 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 - 变量应该以小写字母开头),因为终结器通常在不同的线程中运行。

【讨论】:

  • 就是这样!所以问题不在于 GC,而在于本地类引用!
  • 即使你解决了引用问题,也不能保证 finalize() 方法的执行。看看:stackoverflow.com/questions/20829228/…
  • @ravindra 确实如此,而且这些测试通常会取得一些成功,并且是一种重要的学习工具。不过,正确执行它们很重要。
猜你喜欢
  • 1970-01-01
  • 2014-07-28
  • 2014-03-13
  • 1970-01-01
  • 1970-01-01
  • 2022-06-15
  • 1970-01-01
  • 1970-01-01
  • 2011-04-22
相关资源
最近更新 更多