Gio*_*gio 5 memory garbage-collection memory-leaks listener javafx-8
我对 JavaFx 8 和监听器内存泄漏问题有点困惑。官方文档说:
ObservableValue 存储对侦听器的强引用,这将防止侦听器被垃圾收集并可能导致内存泄漏。
我想举一个例子,其中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());
}
}
Run Code Online (Sandbox Code Playgroud)
我可以得到内存泄漏吗?当一个ConfigurationPane对象被垃圾回收时,这个MyListener对象也被垃圾回收了?我无法看到一个场景
对侦听器的强引用将防止侦听器被垃圾收集
PS我看到其他关于此的SO问题,但这些都没有帮助我理解这个问题。
谢谢。
这意味着存储侦听器的映射没有使用弱引用,您必须自己删除侦听器以避免内存泄漏。
在下面的示例中,尽管相应的 TextFields 已从场景中删除,但 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();
}
}
Run Code Online (Sandbox Code Playgroud)
如果ObservableValue存储对侦听器的弱引用,那么就不会有问题。可以通过下一个示例来模仿:
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();
}
}
Run Code Online (Sandbox Code Playgroud)