为什么这个弱听众不是垃圾收集?

时间:2015-07-28 14:51:30

标签: java listener javafx-8

以下是Kishori Sharan的Learn JavaFX 8书中的一个例子:

package sample;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WeakChangeListener;

public class WeakListener {
    public static IntegerProperty counter = new SimpleIntegerProperty(100);
    public static WeakChangeListener<Number> weakListener;
    public static ChangeListener<Number> changeListener;

    public static void main(String[] args) {
        // Add a weak change listener to the property
        addWeakListener();
        counter.set(300);
        System.gc();
        System.out.println("Garbage collected: " +
                weakListener.wasGarbageCollected());
        counter.set(400);
        changeListener = null;
        System.gc();
        System.out.println("Garbage collected: " +
                weakListener.wasGarbageCollected());
        counter.set(500);
    }

    public static void addWeakListener() {
        changeListener = WeakListener::changed;
        weakListener = new WeakChangeListener<>(changeListener);
        counter.addListener(weakListener);
        counter.set(200);
    }

    public static void changed(ObservableValue<? extends Number> prop,
                               Number oldValue,
                               Number newValue) {
        System.out.print("Counter changed: ");
        System.out.println("old = " + oldValue + ", new = " + newValue);
    }
}

在强侦听器设置为null后,弱侦听器应该是GCed:

enter image description here

但结果与预期不符:

Counter changed: old = 100, new = 200
Counter changed: old = 200, new = 300
Garbage collected: false
Counter changed: old = 300, new = 400
Garbage collected: false
Counter changed: old = 400, new = 500

我在OSX yosemite上使用jdk 1.8_u51。

4 个答案:

答案 0 :(得分:5)

我担心这篇文章是错的 - 这是纠正的句子:

  

将其设置为null然后再次调用垃圾收集后,更改侦听器可能被垃圾收集。

基本上,System.gc();中没有Java合同。请参阅here措辞中的状态(我的重点):

  

调用gc方法建议 Java虚拟机花费大量精力来回收未使用的对象,以使其当前占用的内存可用于快速重用。当控制从方法调用返回时,Java虚拟机已尽最大努力从所有丢弃的对象中回收空间。

没有任何声明它会实际收集任何垃圾。

答案 1 :(得分:2)

简单但令人失望的答案:System.gc()不强制垃圾收集。它所做的就是告诉VM现在是收集垃圾的好时机。 VM现在不必运行垃圾回收。

如果是VM,例如我认为,它有足够的内存,当时可能无法运行垃圾收集。

因此,如果您真的想测试垃圾收集,请提高内存使用率并在较长时间内多次运行System.gc()。这样你可能会增加收听者的机会。

答案 2 :(得分:1)

这里也有同样的问题。

我发现只需从方法引用中更改ChangeListener的创建:

changeListener = WeakListener::changed;

到一个普通的匿名类

changeListener = new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                System.out.print("Counter changed: ");
                System.out.println("old = " + oldValue + ", new = " + newValue);
            }
        };

确实输出得很好:

Counter changed: old = 100, new = 200
Counter changed: old = 200, new = 300
Garbage collected: false
Counter changed: old = 300, new = 400
Garbage collected: true

哇!这是一个与lambda相关的bug吗? 使用win7 x64和jdk8u60

答案 3 :(得分:0)

您在静态变量weakListener中保留对WeakChangeListener的引用。

当对象没有任何引用时,它只被垃圾收集。

当你使changeListener无效时,它保留给weakListener的内部引用会丢失,但只要至少有一个引用,该对象就会保留在内存中。

编辑:

我不知道弱引用如何在Java上运行。如果像我一样,某人不了解他们,https://weblogs.java.net/blog/2006/05/04/understanding-weak-references是一个很好的阅读。