JavaFXのシーングラフでNodeオブジェクトがメモリリークする
JavaFX version: 8
JDK: 1.8
OS: Windows 10
開発中のアプリケーションでメモリリークが起きているため、以下のコードで検証をしました。
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
class CustomNode extends Pane {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Do finalize!!!");
}
}
public class Main extends Application {
private Button aButton = null;
private Button bButton = null;
private Pane container = new Pane();
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
aButton = (Button)root.lookup("#AButton");
bButton = (Button)root.lookup("#BButton");
aButton.setOnAction(event -> {
System.out.println("AOnPushed");
for (int i = 0; i < 10000; i++) {
CustomNode node = new CustomNode();
container.getChildren().add(node);
}
});
bButton.setOnAction(event -> {
System.out.println("BOnPushed");
container.getChildren().removeAll();
});
}
public static void main(String[] args) {
launch(args);
}
}
- Aボタンを押すと、コンテナに生成したNodeオブジェクトを追加する。
- Bボタンを押すと、コンテナを空にする。
- VisualVM等でGCを実行する。
2.
の段階で、コンテナからNodeオブジェクトは破棄されるため、Nodeオブジェクトを参照する要素がなくなる。 -> GCの対象になる。というのが期待する動作です。しかし、実際にはコンテナを空にしてもNodeオブジェクトは破棄されません。
このコードで何故メモリリークが発生してしまうのでしょうか?
追記
package sample;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
class CustomNode {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Do finalize!!!");
}
}
public class Main extends Application {
private Button aButton = null;
private Button bButton = null;
private ObservableList<CustomNode> container = FXCollections.observableArrayList();
// private List<CustomNode> container = new ArrayList<>();
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
aButton = (Button)root.lookup("#AButton");
bButton = (Button)root.lookup("#BButton");
aButton.setOnAction(event -> {
System.out.println("AOnPushed");
for (int i = 0; i < 10000; i++) {
CustomNode node = new CustomNode();
container.add(node);
}
});
bButton.setOnAction(event -> {
System.out.println("BOnPushed");
// container.clear(); // clear()だとリークしなかった
container.removeAll();
});
}
public static void main(String[] args) {
launch(args);
}
}
ObservableList<>を使うだけで同様の現象になったのでNodeクラス自体は関係なさそうです。