Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX eats my memory?

Before going frustrated about the title, I would like to clear out that I am a fresher on JavaFX UI. I have been a developer for 9 years, using Swing and currently I decided to give a try to the JavaFX. Examples on the net shows that JavaFX really can create beautiful GUIs compared to Swing. Maybe I am trying to create and deploy GUIs the wrong way, but one thing is for sure. JavaFX panes load slower than Swing and consumes a lot more memory. The same GUI was redesigned with JAVAFX and it takes almost 200Mb while the Swing GUI take only 50Mb.

Here I give an example of the code of how I create the GUIs programmatically using FXML.

public class PanelCreator {

   private FXMLPane<LoginPaneController>           loginFXML;
   private FXMLPane<RegistrationPaneController>    registerFXML;
   private FXMLPane<EmailValidationPaneController> emailValidationFXML;

   public PanelCreator() {
      this.rootPane = rootPane;
      try {
        loginFXML           = new FXMLPane<LoginPaneController>("Login.fxml");
        registerFXML        = new FXMLPane<RegistrationPaneController>("Register.fxml");
        emailValidationFXML = new FXMLPane<EmailValidationPaneController>("EmailValidation.fxml");
      } catch (IOException e) {e.printStackTrace();} // catch
   } // Constructor Method

   public Pane getLoginPane() {
      return loginFXML.getPane();
   } // getLoginPane()

   public Pane getRegisterPane() {
      return registerFXML.getPane();
   } // getRegisterPane

   public Pane getEmailValidationPane() {
      return emailValidationFXML.getPane();
   } // getEmailValidationPane

   public LoginPaneController getLoginPaneController() {
      return loginFXML.getController();
   } // getLoginPaneController()

   public RegistrationPaneController getRegistrationPaneController() {
      return registerFXML.getController();
   } // getRegistrationPaneController()
} // class PanelCreator

The constructor method of PanelCreator creates 3 FXMLPane classes, a class that combines both the FXML Pane and its Controller. An code of FXMLPane class is shown on the following code.

public class FXMLPane<T> {

    private Pane pane;
    private T paneController;

    public FXMLPane(String url) throws IOException {
        URL location = getClass().getResource(url);
        FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(location);
        fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
        pane = fxmlLoader.load(location.openStream());
        paneController = fxmlLoader.<T>getController();
    } // Constructor Method

    public Pane getPane() {
        return pane;
    } // getPane()

    public T getController() {
        return paneController;
    } // getController()
}

Through PanelCreator now I can use the get methods to get each JavaFX Panel and its controller and I do not have to run the FXML load method every time to get the panel. Currently, what bothers me is not that the creation of FXML GUIs is slower than Swing but more that the RAM is 3x and 4x times more than the correspoing Swing version.

Can someone explain to me what I am doing wrong? The FXML files have just basic components on a Grid Pane, components like buttons, layers and textfields.

Memory Consumption Between Java and JavaFX

The code for the above example can be found here

like image 838
javasuns Avatar asked Mar 04 '15 19:03

javasuns


1 Answers

Summarizing the answers from the comment section:

  • JavaFX needs more memory in general. E.g. JavaFX uses double precision for all properties along the UI-components, while Swing uses integer values most of the time. But the difference should not be noticeable.
  • Java consumes more memory as it needs to. As a default Java does not return memory back to your system even if you trigger the garbage collection. Thus if a JavaFX program needs a lot of memory on the initialization process but frees it afterwards, the JRE continues to hold the maximum level of memory for ever (see picture 1). As a side effect the GC will be triggered less often, because there is so much free unused memory (see picture 2). You can change the default by using the JVM option -XX:+UseG1GC. This changes the behavior of how memory is allocated, how it's freed and when the GC is triggered. With this option the allocated memory should better fit in with the used memory. If you want more tuning see Java Heap Tuning
  • JavaFX is a new framework compared to Swing. It will be improved over time in performance and resources consumption. As you can see in picture 1 and 3 it has already been improved. It now uses 8 to 9MB of memory on a 64Bit Linux machine. This is even less memory than the Swing version. I used Oracle Java

    java version "1.8.0_111"
    Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
    Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
    

Memory consumption over time for the JavaFX example program. It shows a huge amount of free memory compared to the used memory.

Picture 1: Memory consumption over time for the JavaFX example program. It shows a huge amount of free memory compared to the used memory. The GC was triggered manually multiple times to show used memory part without garbage.

Memory consumption over time for the JavaFX example program, but without manually triggering the GC.

Picture 2: Memory consumption over time for the JavaFX example program, but without manually triggering the GC. The used memory grows and grows because the GC isn't triggered.

Memory consumption over time for the JavaFX example program using the GC option -XX:+UseG1GC

Picture 3: Memory consumption over time for the JavaFX example program using the GC option -XX:+UseG1GC. After the first GC cycle the memory size was reduced to fit the real size of used memory.

like image 103
Zomono Avatar answered Nov 09 '22 01:11

Zomono