我对
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();
}
}