JDK-8218124 : SwingNode Memory Leak
  • Type: Bug
  • Component: javafx
  • Sub-Component: swing
  • Affected Version: 8,openjfx11
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: x86_64
  • Submitted: 2019-01-30
  • Updated: 2021-03-01
  • Resolved: 2020-09-22
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
openjfx16Resolved
Related Reports
Duplicate :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
If I Open a new Stage with a SwingNode and close it. The SwingNode stays in the Heap but it isn't reachable anymore.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Open a new Window with the Button
Close the Window with X

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The SwingNode is not reachable anymore and should be disposed
ACTUAL -
The SwingNode stays in the heap and is never disposed

---------- BEGIN SOURCE ----------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.SwingUtilities;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
 *
 */
public class JFXDemo extends Application {

	Stage stage;

	/**
	 * @param args
	 */
	public static void main( final String[] args ) {
		launch( args );
	}

	@SuppressWarnings("restriction")
	@Override
	public void start( final Stage stage ) {
		final SwingNode swingNode = new SwingNode();

		createSwingContent( swingNode );

		final StackPane pane = new StackPane();
		pane.getChildren().add( swingNode );

		stage.setTitle( "Swing in JavaFX" );
		stage.setScene( new Scene( pane, 250, 150 ) );
		stage.show();
	}

	private void createSwingContent( final SwingNode swingNode ) {
		SwingUtilities.invokeLater( new Runnable() {
			@Override
			public void run() {
				final JButton button = new JButton( "Click" );
				button.addActionListener( new ActionListener() {

					@Override
					public void actionPerformed( final ActionEvent e ) {

						final SwingNode swingNode = new SwingNode();

						createSwingContent( swingNode );

						final StackPane pane = new StackPane();
						pane.getChildren().add( swingNode );
						Platform.runLater( () -> {
							new Runnable() {
								@Override
								public void run() {
									Stage myStage;
									myStage = new Stage();
									myStage.setTitle( "Swing in JavaFX" );
									myStage.setScene( new Scene( pane, 250, 150 ) );
									myStage.show();

								}
							}.run();
						} );

					}
				} );
				swingNode.setContent( button );

			}
		} );
	}
}
---------- END SOURCE ----------

FREQUENCY : always



Comments
[~kcr] I am proposing to close this "Not an issue" , as, if we do stage.close(), it GCs SwingNode, as can be seen in attached SwingNodeSetContentMemoryLeakTest.java.
25-06-2020

It seems in the description, it was mentioned that "even" if the stage is closed, the swingNode is not collected. But the testcase does not actually close the stage. I have written automated JUnit test SwingNodeSetContentMemoryLeakTest.java which actually passes if we do a stage.close() So, not sure if this issue is actually a JBS issue. If stage.close() is not mandatory to expect swingnode collection during GC (ie, by just clicking on close X button) we need to add SwingNode.java =============== private void disposeLwFrame() { System.out.println("disposeLwFrame"); if (lwFrame == null) { return; } SwingNodeHelper.runOnEDT(() -> { if (lwFrame != null) { swNodeIOP.removeWindowFocusListener(lwFrame, snfListener); snfListener = null; swNodeIOP.disposeFrame(lwFrame); lwFrame = null; + swNodeIOP = null; } }); } this line in addition to attached patches so as to make SwingNode get collected, but I am not sure it's correct, although now in jvisualvm, the SwingNode instances comes down to 1(which is the parent SwingNode) if we close by clicking on X. @kcr, can you please comment if Stage.close() is mandatory to expect SwingNode (which is a Node) disposal?
22-06-2020

I used jdk1.8.0_251 jvisualvm which shows Memory sampling: Available (perform GC not supported, thread memory allocation not supported). Not sure why "Perform GC" is not supported now. I added a method in JFXDemo to do System.gc() followed by System.runFinalization() every 5secs but it does not collect SwingNodes even though there is no "referent" in SwingNode instances.
19-06-2020

From jvisualvm, I see the heap dump instances of SwingNode has "referent" pointing to 3 cases [I think that "referent" will point to strong references holding on to the objects] 1. SwingNodeInteropN#SwingNodeContent pointing to LightweightContentWrapper#LightweightContentProxy 2. SwingNode#SwingNodeWindowFocusListener 3. Disposer$Hashtable I made changes to both JDK and FX so that 1. LightweightContentProxy now uses a WeakReference<LightweightContentWrapper> 2. removeWindowFocusListener() is added to dispose SwingNodeWindowFocusListener when we dispose lightweightFrame 3. Use "swnodeIOP" ie SwingNode target instead of "this" when we pass the target to Disposer.addRecord() With this, there are no "referent" in heap dump but still number of SwingNode instances are not coming down. Maybe because "Perform GC" button in jvisualvm is disabled in my case, not sure why. [~kcr] when you have time, could you comment on the patches?
18-06-2020

We do dispose of the LightweightFrame before setting the content. Since content is JComponent, there's no dispose() for that. I also tried WeakReference of JComponent but to no effect. private void setContentImpl(JComponent content1) { if (lwFrame != null) { swNodeIOP.disposeFrame(lwFrame); lwFrame = null; } WeakReference<JComponent> contentRef = new WeakReference<JComponent>(content1); JComponent content = contentRef.get(); if (content != null) { .....................
17-06-2020

Since this was recently reported again, we should try to get this fixed for JavaFX 15.
11-04-2020

See JDK-8241972 for another test case.
11-04-2020

Issue is reproducible in JDK 8, openjfx 11.0.2. With each click on the button 'Click', swingNode instance increases by 1 in visualVM memory profiler. Even after closing all windows (except parent) and clicking on 'perform GC' in visulaVM, swingnode instances does not come down to 0. Running testcase using "-Djavafx.embed.singleThread=true" didn't make any difference in the results.
31-01-2019