JavaFX和侦听器内存泄漏

我对
JavaFx 8和监听器内存泄漏问题有点困惑.官方
doc说:

The ObservableValue stores a strong reference to the listener which will prevent the listener from being garbage collected and may result in a memory leak.

我想有一个使用ObservableValue< T>的例子. addListener方法创建内存泄漏.

例如,如果我有这样的类:

public class ConfigurationPane extends AnchorPane {
    @FXML
    private Label titleLabel;

    public ConfigurationPane () {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("view/ConfigurationPane .fxml"));
    fxmlLoader.setRoot(this);
    fxmlLoader.setController(this);
    try {
        fxmlLoader.load();
    } catch (IOException e) {
          e.printStackTrace();
      }
}

    @FXML
    private void initialize() {
        titleLabel.sceneProperty().addListener(new MyListener());
    }
}

我可以得到内存泄漏吗?当一个ConfigurationPane对象被垃圾收集时,MyListener对象也被垃圾收集了吗?我无法看到一个场景

a strong reference to the listener will prevent the listener from being garbage collected

附:我看到其他S.O.关于这一点的问题,但这些都没有帮助我理解这个问题.

谢谢.

最佳答案 这意味着存储侦听器的映射不使用弱引用,您必须自己删除侦听器以避免内存泄漏.

在下面的示例中,尽管从场景中删除了相应的TextField,但LeakingListener对象永远不会被释放:

public class LeakListener extends Application {

    private static class LeakingListener implements InvalidationListener {

        private final TextField tf;
        private final int[] placeHolder = new int[50000]; // to simplify monitoring

        public LeakingListener(TextField tf) {
            this.tf = tf;
        }

        public void invalidated(Observable i) {
            tf.setText(tf.getText() + ".");
        }
    }

    @Override
    public void start(Stage primaryStage) {
        final Pane root = new VBox(3);

        final Button btnType = new Button("Type in all");

        Button btnAdd = new Button("Add");
        btnAdd.setOnAction((e) -> {
            TextField tf = new TextField();
            root.getChildren().add(tf);
            // memory leaking listener which never gets cleaned
            btnType.armedProperty().addListener(new LeakingListener(tf));
        });

        Button btnRemove = new Button("Remove");
        btnRemove.setOnAction((ActionEvent e) -> {
            // find random TextEdit element
            Optional<Node> toRemove = root.getChildren().stream().filter((Node t) -> t instanceof TextField).findAny();
            // if any, and remove it
            if (toRemove.isPresent()) {
                root.getChildren().remove(toRemove.get());
            }
        });

        Button btnMemory = new Button("Check Memory");
        btnMemory.setOnAction((e) -> {
            System.gc();
            System.out.println("Free memory (bytes): " + Runtime.getRuntime().freeMemory());
        });

        root.getChildren().addAll(btnAdd, btnRemove, btnType, btnMemory);
        Scene scene = new Scene(root, 200, 350);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

如果ObservableValue将weak reference存储到侦听器,则不会有问题.它可以通过下一个例子来模仿:

public class LeakListener extends Application {

    private static class NonLeakingListener implements InvalidationListener {

        // we need listener to don't hold reference on TextField as well
        private final WeakReference<TextField> wtf;
        private final int[] placeHolder = new int[10000];

        public NonLeakingListener(TextField tf) {
            this.wtf = new WeakReference<>(tf);
        }

        public void invalidated(Observable i) {
            if (wtf.get() != null) {
                wtf.get().setText(wtf.get().getText() + ".");
            }
        }
    }

    @Override
    public void start(Stage primaryStage) {
        final Pane root = new VBox(3);

        final Button btnType = new Button("Type in all");

        // Here is rough weak listeners list implementation
        WeakHashMap<TextField, NonLeakingListener > m = new WeakHashMap<>();
        btnType.armedProperty().addListener((e)-> {
            for (TextField tf : m.keySet()) {
                m.get(tf).invalidated(null);
            }
        });


        Button btnAdd = new Button("Add");
        btnAdd.setOnAction((e) -> {
            TextField tf = new TextField();
            root.getChildren().add(tf);
            m.put(tf, new NonLeakingListener(tf));
        });

        Button btnRemove = new Button("Remove");
        btnRemove.setOnAction((e) -> {
            // find random TextEdit element
            Optional<Node> toRemove = root.getChildren().stream().filter((Node t) -> t instanceof TextField).findAny();
            // if any, and remove it
            if (toRemove.isPresent()) {
                root.getChildren().remove(toRemove.get());
            }
        });

        Button btnMemory = new Button("Check Memory");
        btnMemory.setOnAction((e)-> {
            System.gc();
            System.out.println("Free memory (bytes): " + Runtime.getRuntime().freeMemory());
        });

        root.getChildren().addAll(btnAdd, btnRemove, btnType, btnMemory);
        Scene scene = new Scene(root, 200, 350);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}
点赞