Ironcache
Ironcache

Reputation: 1759

Multiple SwingNode Cause Extreme Performance Degradation

When I have multiple SwingNodes in a panel (GridPane in a JFXPanel), I notice an extreme performance degradation. This doesn't appear to occur if there's only ONE SwingNode.

I realize that there's some expected degradation when mixing Swing and JavaFX, but this renders the application pretty much unusable (and I can't really change my industry circumstances; the legacy code is still in Swing, but we really want the new JavaFX graphing utilities).

This is running on Windows 7 on Java 8u60.

MCVE:

import javax.swing.*;

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;

public class SwingNodeTest {
    private static Scene createScene(JComponent button1, JComponent button2) {
        GridPane pane = new GridPane();
        pane.getColumnConstraints().add(new ColumnConstraints(100));
        pane.getColumnConstraints().add(new ColumnConstraints(200));

        SwingNode node1 = new SwingNode();
        // Best practice to call SwingNode->setContent(...) on the EDT, but doesn't make
        // a difference for the test.
        node1.setContent(button1);
        pane.add(node1, 0, 0);

        ToggleButton node2 = new ToggleButton("2");
        // Commenting out the above line and uncommenting the below lines cause EXTREME
        // Performance degradation.
//        SwingNode node2 = new SwingNode();
//        node2.setContent(button2);
        pane.add(node2, 1, 0);

        return new Scene(pane);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                JFXPanel panel = new JFXPanel();
                frame.setSize(800, 600);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setContentPane(panel);
                frame.setVisible(true);

                JButton button1 = new JButton("1");
                JToggleButton button2 = new JToggleButton("2");

                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        final Scene scene = createScene(button1, button2);
                        panel.setScene(scene);
                    }
                });
            }
        });
    }
}

EDIT: Just realized after letting this MCVE run for a few minutes, I get OutOfMemoryErrors (GC overhead limit exceeded).

Upvotes: 3

Views: 1115

Answers (1)

Ironcache
Ironcache

Reputation: 1759

I submitted this as a bug. As of the time of writing, it's currently open and in the JDK backlog:

https://bugs.openjdk.java.net/browse/JDK-8144504

Unfortunately, while this bug is open, the only work-around seems to be to limit your Swing to JavaFX to Swing integration. We ended up remaking all of our Swing widgets that were being used in the JavaFX panel in JavaFX, which was a fairly substantial overhaul.

For the reference of anyone who wandered in here looking for a solution to this problem, I'm also going to post this JDK bug, which is also open as of the time of writing:

https://bugs.openjdk.java.net/browse/JDK-8136530

The bug states that, even if you only have one SwingNode in a JavaFX Pane in a JFXPanel, your CPU usage will dramatically increase (but the freezing of multiple nodes does not occur). This ended up biting us after we thought we cleverly got around the issue by limiting the number of SwingNode instances in our JavaFX Pane. Hopefully, this heads up will help prevent others from face-planting into it like we did.

EDIT (21/09/2017): The freezing bug is now listed as fixed with a fix version of Java 10. The CPU usage increase bug is still in an open state.

Upvotes: 1

Related Questions