I have a JavaFX application that displays a Swing plot via a SwingNode. Due to the way that the swing component is written and my unwillingness to refactor it, I create a new instance of the swing plot each time the user needs to update the data. In other words, whenever the user re-generates the plot, it creates a new Swing component and sets the SwingNode's content to the new component.
It all works fine except that I discovered that the swing components never get garbage collected. They contain a substantial amount of data, so after a while it becomes a pretty severe memory leak.
I've been able to demonstrate the issue with this minimum reproducible example:
public class LeakDemo extends Application {
//Keep week references to all panels that we've ever generated to see if any
//of them get collected.
private Collection<WeakReference<JPanel>> panels = new CopyOnWriteArrayList<>();
@Override
public void start(Stage primaryStage) throws Exception {
SwingNode node = new SwingNode();
Pane root = new Pane();
root.getChildren().add(node);
//Kick off a thread that repeatedly creates new JPanels and resets the swing node's content
new Thread(() -> {
while(true) {
//Lets throw in a little sleep so we can read the output
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(() -> {
JPanel panel = new JPanel();
panels.add(new WeakReference<>(panel));
node.setContent(panel);
});
System.out.println("Panels in memory: " + panels.stream().filter(ref -> ref.get() != null).count());
//I know this doesn't guarantee anything, but prompting a GC gives me more confidence that this
//truly is a bug.
System.gc();
}
}).start();
primaryStage.setScene(new Scene(root, 100, 100));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The output of the program is
Panels in memory: 0
Panels in memory: 1
Panels in memory: 2
Panels in memory: 3
Panels in memory: 4
Panels in memory: 5
Panels in memory: 6
Panels in memory: 7
Panels in memory: 8
Panels in memory: 9
Panels in memory: 10
and will continue like that into the thousands.
I tried inspecting a heap dump from jvisualvm, but got pretty lost in the sea of references.
I'm suspicious that this is an issue in JavaFX, but I thought I'd check here before I report it as a bug.