FULL PRODUCT VERSION :
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.15063]
A DESCRIPTION OF THE PROBLEM :
If a stage is never shown, a SwingNode will never be elgible for GC. If the stage is shown, it will eventually be removed by the GC
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The memory leak is evident in a profiler. I used the NB profiler.
Run the test program. Keep pressing the "Open Stage" button. Doing so will continually open intermediate stages, closing an existing one if it exists.
If the intermediate stage was being shown (the stage.show() method at line 100 is not commented out), the SwingNodes will be collect appropriately. If the intermediate stages are not shown (the stage.show() method at line 100 is commented out), they will never be collected.
Don't forget to use the -Djavafx.embed.singleThread=true option.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The SwingNodes will eventually be collected
ACTUAL -
No SwingNodes are collected if the intermediate stages are not shown
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javafxmemoryleak;
import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.swing.JLabel;
public class JavaFXMemoryLeak extends Application {
private Stage tempStage;
@Override
public void start(final Stage primaryStage) {
Button openDialog = new Button();
openDialog.setText("Open Dialog");
openDialog.setOnAction(e -> openDialog(primaryStage));
Button openStage = new Button();
openStage.setText("Open Stage");
openStage.setOnAction(e -> openStage(primaryStage));
Button closeStage = new Button();
closeStage.setText("Close Stage");
closeStage.setOnAction(e -> closeStage());
HBox root = new HBox();
root.getChildren().add(openDialog);
root.getChildren().add(openStage);
root.getChildren().add(closeStage);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Memory Test");
primaryStage.setScene(scene);
primaryStage.show();
}
private void closeStage() {
if (tempStage != null)
tempStage.close();
tempStage = null;
}
private void openDialog(Stage primaryStage) {
StackPane root = new StackPane();
ProgressIndicator pi = new ProgressIndicator();
pi.parentProperty().addListener((obs, oldVal, newVal) -> System.out.println("Parent: " + newVal));
pi.sceneProperty().addListener((obs, oldVal, newVal) -> {
System.out.println("Dialog Scene: " + newVal);
});
root.getChildren().add(pi);
Alert dialog = new Alert(Alert.AlertType.INFORMATION);
dialog.getDialogPane().setContent(root);
dialog.initOwner(primaryStage);
dialog.showAndWait();
}
private void openStage(Stage primaryStage) {
closeStage();
BorderPane root = new BorderPane();
SwingNode sw = new SwingNode();
sw.setContent(new JLabel("SWING"));
root.centerProperty().set(sw);
// ProgressIndicator pi = new ProgressIndicator();
// pi.parentProperty().addListener((obs, oldVal, newVal) -> System.out.println("Parent: " + newVal));
// pi.sceneProperty().addListener((obs, oldVal, newVal) -> {
// System.out.println("Scene: " + newVal);
// });
// root.centerProperty().set(pi);
final Stage stage = new Stage();
Button btn = new Button();
btn.setText("Stage Close");
btn.setOnAction(e -> {
stage.close();
});
root.bottomProperty().set(btn);
Scene scene = new Scene(root, 150, 100);
stage.setScene(scene);
tempStage = stage;
//If we don't show the stage, resources are never released
// stage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("java.version = " + System.getProperty("java.version"));
System.out.println("java.runtime.version = " + System.getProperty("java.runtime.version"));
launch(args);
}
}
---------- END SOURCE ----------