ADDITIONAL SYSTEM INFORMATION :
OS Windows 10
Tested on:
* OpenJDK/OpenJFX 8.0.271-b08
* Liberica JDK/JFX combined build 15.0.1+b01
* OpenJDK JDK 17 early access build 10 with OpenJFX 16 build 7
A DESCRIPTION OF THE PROBLEM :
When setting the content of a SwingNode, the old content is not garbage collected.
There are some other bugs that appear related, but are marked as resolved and this is definitely not resolved. I'm not sure if this is truly a separate bug, or if one of those bugs should be reopened. Here are the related bugs:
* https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8093738
* https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8241972
* https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8218124
So far my debugging (using JDK 1.8.0.271) has shown the references to old content are kept alive in a static list of windows in the GlassStage class. The should be removed when close() is called on the JComponent, but this doesn't seem to be called via SwingNode.setContent().
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the following program listed in the "source code for an executable test case" section.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Should print that there are 1 or 2 panels in memory. Possibly a few more but the number should not trend upward over time.
ACTUAL -
Prints:
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
...
And goes on forever (or I suppose until it runs out of memory, but I didn't wait that long).
---------- BEGIN SOURCE ----------
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
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);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
If you place a JComponent (such as a JPanel) as the content of the swing node, and then update the content of that JComponent rather than setting the SwingNode's content directly, it will collect the old content appropriately. See https://stackoverflow.com/questions/66270491/content-of-swingnode-not-garbage-collected-when-content-changed
FREQUENCY : always
OS Windows 10
Tested on:
* OpenJDK/OpenJFX 8.0.271-b08
* Liberica JDK/JFX combined build 15.0.1+b01
* OpenJDK JDK 17 early access build 10 with OpenJFX 16 build 7
A DESCRIPTION OF THE PROBLEM :
When setting the content of a SwingNode, the old content is not garbage collected.
There are some other bugs that appear related, but are marked as resolved and this is definitely not resolved. I'm not sure if this is truly a separate bug, or if one of those bugs should be reopened. Here are the related bugs:
* https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8093738
* https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8241972
* https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8218124
So far my debugging (using JDK 1.8.0.271) has shown the references to old content are kept alive in a static list of windows in the GlassStage class. The should be removed when close() is called on the JComponent, but this doesn't seem to be called via SwingNode.setContent().
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the following program listed in the "source code for an executable test case" section.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Should print that there are 1 or 2 panels in memory. Possibly a few more but the number should not trend upward over time.
ACTUAL -
Prints:
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
...
And goes on forever (or I suppose until it runs out of memory, but I didn't wait that long).
---------- BEGIN SOURCE ----------
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
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);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
If you place a JComponent (such as a JPanel) as the content of the swing node, and then update the content of that JComponent rather than setting the SwingNode's content directly, it will collect the old content appropriately. See https://stackoverflow.com/questions/66270491/content-of-swingnode-not-garbage-collected-when-content-changed
FREQUENCY : always
- duplicates
-
JDK-8284352 JLightweigthFrame is not cleared when SwingNode.setContent() is called
- Closed
- relates to
-
JDK-8093738 [SwingNode] Calling setContent method repeatedly produces memory leak
- Closed
-
JDK-8218124 SwingNode Memory Leak
- Closed
-
JDK-8241972 SwingNode memory leak
- Closed
-
JDK-8315317 Add test for JDK-8262518
- Resolved
(2 links to)