1 2 Previous Next 18 Replies Latest reply: Jan 29, 2013 11:46 AM by drenda81 Go to original post RSS
      • 15. Re: Heap space with Gui
        drenda81
        Hi James,
        I made another test to understand the problem of css. I remove the code that add the style to the Scene. I leave only the css in the fxml. After some switch from one view to another the javafx's object CascadeStyle grow.
        This is the tree of allocation: http://justpaste.it/1ufy

        Seems that the style is linked to the scene and not to the anchor panel of the view. This could be a further proof of the fact that it is a bug?

        Thanks
        • 16. Re: Heap space with Gui
          James_D
          I created a JIRA: http://javafx-jira.kenai.com/browse/RT-27896

          I have a test case:

          MemoryLeakTest.java
          import java.net.URL;
          
          import javafx.application.Application;
          import javafx.application.Platform;
          import javafx.beans.property.IntegerProperty;
          import javafx.beans.property.SimpleIntegerProperty;
          import javafx.concurrent.Task;
          import javafx.fxml.FXMLLoader;
          import javafx.scene.Parent;
          import javafx.scene.Scene;
          import javafx.scene.control.Label;
          import javafx.scene.layout.BorderPane;
          import javafx.stage.Stage;
          
          public class MemoryLeakTest extends Application {
          
               @Override
               public void start(Stage primaryStage) throws Exception {
                    System.out.println(com.sun.javafx.runtime.VersionInfo.getRuntimeVersion());
                    final URL fxml = getClass().getResource("Test.fxml");
                    final String cssLoc = getClass().getResource("test.css").toExternalForm();
                    final BorderPane root = new BorderPane();
                    final Label label = new Label();
                    root.setTop(label);
                    final Scene scene = new Scene(root, 600, 400);
                    primaryStage.setScene(scene);
                    primaryStage.show();
          
                    final IntegerProperty count = new SimpleIntegerProperty(1);
                    Task<Void> task = new Task<Void>() {
                         @Override
                         protected Void call() throws Exception {
                              while (! isCancelled()) {
                                   try {
                                        Thread.sleep(100);
                                   } catch (InterruptedException exc) {
                                        if (isCancelled()) {
                                             break ;
                                        }
                                   }
                                   Platform.runLater(new Task<Void>() {
                                        @Override
                                        public Void call() throws Exception {
                                             final Parent content = FXMLLoader.<Parent>load(fxml);
                                             content.getStylesheets().add(cssLoc);
                                             root.setCenter(content);
                                             return null ;
                                        }
                                   });
                                   updateMessage("Screen "+count.get()+"\n"+memInfo());
                                   count.set(count.get()+1);
                              }
                              return null;
                         }
                    };
                    label.textProperty().bind(task.messageProperty());
                    Thread t = new Thread(task);
                    t.setDaemon(true);
                    t.start();
               }
               
               private String memInfo() {
                    Runtime rt = Runtime.getRuntime();
                    long max = rt.maxMemory();
                    long total = rt.totalMemory();
                    long free = rt.freeMemory() ;
                    long used = total - free ;
                    return String.format("Max: %,d Total: %,d Used: %,d Free: %,d", max, total, used, free);
               }
          
               public static void main(String[] args) {
                    launch(args);
               }
          }
          Test.fxml
          <?xml version="1.0" encoding="UTF-8"?>
          
          <?import javafx.scene.layout.AnchorPane?>
          <?import javafx.scene.layout.VBox?>
          <?import javafx.scene.layout.HBox?>
          <?import javafx.scene.control.TextField?>
          <?import javafx.scene.control.Label?>
          <?import javafx.scene.control.Button?>
          <?import javafx.scene.control.ListView?>
          <?import javafx.collections.FXCollections?>
          <?import javafx.scene.layout.Priority?>
          <?import javafx.scene.control.Control?>
          <?import java.lang.String?>
          <?import java.net.URL?>
          
          <VBox xmlns:fx="http://javafx.com/fxml" styleClass="vbox">
               <children>
                    <Label text="Fruits" />
                    <TextField promptText="Input Fruit Here">
                         <minHeight><Control fx:constant="USE_PREF_SIZE"/></minHeight>
                    </TextField>
          
                    <ListView>
                         <items>
                              <FXCollections fx:factory="observableArrayList" fx:id="listItems">
                                   <String fx:value="Apples"/>
                                   <String fx:value="Oranges"/>
                                   <String fx:value="Pears"/>
                                   <String fx:value="Bananas"/>
                                   <String fx:value="Grapes"/>
                                   <String fx:value="Pomegranates"/>
                                   <String fx:value="Mangoes"/>
                              </FXCollections>                    
                         </items>
                    </ListView>
                    <HBox>
                         <children>
                              <Button text="OK"/>
                              <Button text="Cancel"/>
                              <Button text="Help..."/>
                         </children>
                    </HBox>
               </children>
          </VBox>
          test.css
          .vbox {
               -fx-border-style : solid ;
               -fx-border-color: cornflowerblue ;
               -fx-border-width: 5 ;
          }
          .label {
               -fx-background-color: cornsilk  ;
          }
          .list-cell:even {
              -fx-background-color: aliceblue ;
              -fx-text-fill: darkgrey  ;
          }
          .list-cell:odd {
               -fx-background-color: azure ;
               -fx-text-fill: darkgrey ;
          }
          .button {
               -fx-base: antiquewhite ;
          }
          The test case takes quite a long time to total failure on my system (about 7000 iterations), but the memory leak is pretty clear to see.

          I think your workaround is to load all your stylesheets at startup and associate them with the Scene. Remove the css from the fxml.
          • 17. Re: Heap space with Gui
            drenda81
            Hi James,
            thanks for your support. I am happy that with your help we found a bug not yet been reported.
            I'm sorry but maybe I found another strange thing....maybe another bug? I've a simple test case. In my test case there are 2 view (I copy the login example of java fx and I've modify it a bit):

            -Login: there is a button, when you click it you go to the profile view
            -Profile: there is a table with some fake data (100 entry), there is also a button to return to the login view

            The problem: if you switch from login view to profile with clicking the buttons, there is no problem (you can see the objects alive with profile). Check this by switching 10-20 times, the memory don't grow and the gc works fine.
            On the table there is a listener on MouseClick, so if you click on the table if calls the same method of the button to return to the login page: gotoLogin(MouseEvent event) {...}. If you switch several times from profile to login page clicking on the table you can see that the object to type ProfileController are not finalized (I put a log in the finalize() method) so you walk with little steps towards the heap space.
            Seems that the listener of the table refers some data that prevent the gc to finalize the objects.

            Main.java
            package it.test;
            
            import java.net.URL;
            import java.util.ResourceBundle;
            
            import javafx.animation.FadeTransition;
            import javafx.beans.property.SimpleStringProperty;
            import javafx.collections.FXCollections;
            import javafx.collections.ObservableList;
            import javafx.fxml.FXML;
            import javafx.fxml.Initializable;
            import javafx.scene.control.Button;
            import javafx.scene.control.CheckBox;
            import javafx.scene.control.Hyperlink;
            import javafx.scene.control.Label;
            import javafx.scene.control.TableColumn;
            import javafx.scene.control.TableView;
            import javafx.scene.control.TextArea;
            import javafx.scene.control.TextField;
            import javafx.scene.control.cell.PropertyValueFactory;
            import javafx.scene.input.MouseEvent;
            import javafx.scene.layout.AnchorPane;
            import javafx.util.Duration;
            
            import org.apache.log4j.Logger;
            
            /**
             * Profile Controller.
             */
            public class ProfileController extends AnchorPane implements Initializable {
                 private Logger log = Logger.getLogger(ProfileController.class);
            
                 @FXML
                 private TextField user;
                 @FXML
                 private TextField phone;
                 @FXML
                 private TextField email;
                 @FXML
                 private TextArea address;
                 @FXML
                 private CheckBox subscribed;
                 @FXML
                 private Hyperlink logout;
                 @FXML
                 private Button save;
            
                 @FXML
                 private Label success;
            
                 @FXML
                 // fx:id="column1"
                 private TableColumn column1; // Value injected by FXMLLoader
            
                 @FXML
                 // fx:id="column2"
                 private TableColumn column2; // Value injected by FXMLLoader
            
                 @FXML
                 // fx:id="column3"
                 private TableColumn column3; // Value injected by FXMLLoader
            
                 @FXML
                 // fx:id="table"
                 private TableView<Person> table; // Value injected by FXMLLoader
            
                 private Main application;
            
                 public void setApp(Main application) {
                      this.application = application;
                      user.setText("");
                      email.setText("");
                      phone.setText("");
            
                      address.setText("");
            
                      success.setOpacity(0);
                 }
            
                 @Override
                 public void initialize(URL location, ResourceBundle resources) {
                      // POPULATE THE TABLE WITH FAKE DATA
                      final ObservableList<Person> data = FXCollections.observableArrayList();
                      for (int i = 0; i < 100; i++) {
                           data.add(new Person("Jacob", "Smith", "jacob.smith@example.com"));
                      }
            
                      column1.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
                      column2.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
                      column3.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
                      table.setItems(data);
            
                 }
            
                 public void gotoLogin(MouseEvent event) {
                      application.gotoLogin();
                 }
            
                 private void animateMessage() {
                      FadeTransition ft = new FadeTransition(Duration.millis(1000), success);
                      ft.setFromValue(0.0);
                      ft.setToValue(1);
                      ft.play();
                 }
            
                 public static class Person {
                      private final SimpleStringProperty firstName;
                      private final SimpleStringProperty lastName;
                      private final SimpleStringProperty email;
            
                      private Person(String fName, String lName, String email) {
                           this.firstName = new SimpleStringProperty(fName);
                           this.lastName = new SimpleStringProperty(lName);
                           this.email = new SimpleStringProperty(email);
                      }
            
                      public String getFirstName() {
                           return firstName.get();
                      }
            
                      public void setFirstName(String fName) {
                           firstName.set(fName);
                      }
            
                      public String getLastName() {
                           return lastName.get();
                      }
            
                      public void setLastName(String fName) {
                           lastName.set(fName);
                      }
            
                      public String getEmail() {
                           return email.get();
                      }
            
                      public void setEmail(String fName) {
                           email.set(fName);
                      }
            
                 }
            
                 @Override
                 protected void finalize() throws Throwable {
                      super.finalize();
                      log.debug("*** FINALIZE Profile***");
                 }
            
            }
            LoginController.java
            package it.test;
            
            import java.net.URL;
            import java.util.ResourceBundle;
            
            import javafx.event.ActionEvent;
            import javafx.fxml.FXML;
            import javafx.fxml.Initializable;
            import javafx.scene.control.Button;
            import javafx.scene.control.Label;
            import javafx.scene.control.PasswordField;
            import javafx.scene.control.TextField;
            import javafx.scene.layout.AnchorPane;
            
            import org.apache.log4j.Logger;
            
            /**
             * Login Controller.
             */
            public class LoginController extends AnchorPane implements Initializable {
                 private Logger log = Logger.getLogger(LoginController.class);
            
                 @FXML
                 TextField userId;
                 @FXML
                 PasswordField password;
                 @FXML
                 Button login;
                 @FXML
                 Label errorMessage;
            
                 private Main application;
            
                 public void setApp(Main application) {
                      this.application = application;
                 }
            
                 @Override
                 public void initialize(URL location, ResourceBundle resources) {
                      errorMessage.setText("");
                      userId.setPromptText("demo");
                      password.setPromptText("demo");
            
                 }
            
                 public void processLogin(ActionEvent event) {
                      application.gotoProfile();
                 }
            
                 @Override
                 protected void finalize() throws Throwable {
                      super.finalize();
                      log.debug("*** FINALIZE Login***");
                 }
            
            }
            ProfileController.java
            package it.test;
            
            import java.net.URL;
            import java.util.ResourceBundle;
            
            import javafx.animation.FadeTransition;
            import javafx.beans.property.SimpleStringProperty;
            import javafx.collections.FXCollections;
            import javafx.collections.ObservableList;
            import javafx.fxml.FXML;
            import javafx.fxml.Initializable;
            import javafx.scene.control.Button;
            import javafx.scene.control.CheckBox;
            import javafx.scene.control.Hyperlink;
            import javafx.scene.control.Label;
            import javafx.scene.control.TableColumn;
            import javafx.scene.control.TableView;
            import javafx.scene.control.TextArea;
            import javafx.scene.control.TextField;
            import javafx.scene.control.cell.PropertyValueFactory;
            import javafx.scene.input.MouseEvent;
            import javafx.scene.layout.AnchorPane;
            import javafx.util.Duration;
            
            import org.apache.log4j.Logger;
            
            /**
             * Profile Controller.
             */
            public class ProfileController extends AnchorPane implements Initializable {
                 private Logger log = Logger.getLogger(ProfileController.class);
            
                 @FXML
                 private TextField user;
                 @FXML
                 private TextField phone;
                 @FXML
                 private TextField email;
                 @FXML
                 private TextArea address;
                 @FXML
                 private CheckBox subscribed;
                 @FXML
                 private Hyperlink logout;
                 @FXML
                 private Button save;
            
                 @FXML
                 private Label success;
            
                 @FXML
                 // fx:id="column1"
                 private TableColumn column1; // Value injected by FXMLLoader
            
                 @FXML
                 // fx:id="column2"
                 private TableColumn column2; // Value injected by FXMLLoader
            
                 @FXML
                 // fx:id="column3"
                 private TableColumn column3; // Value injected by FXMLLoader
            
                 @FXML
                 // fx:id="table"
                 private TableView<Person> table; // Value injected by FXMLLoader
            
                 private Main application;
            
                 public void setApp(Main application) {
                      this.application = application;
                      user.setText("");
                      email.setText("");
                      phone.setText("");
            
                      address.setText("");
            
                      success.setOpacity(0);
                 }
            
                 @Override
                 public void initialize(URL location, ResourceBundle resources) {
                      // POPULATE THE TABLE WITH FAKE DATA
                      final ObservableList<Person> data = FXCollections.observableArrayList();
                      for (int i = 0; i < 100; i++) {
                           data.add(new Person("Jacob", "Smith", "jacob.smith@example.com"));
                      }
            
                      column1.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
                      column2.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
                      column3.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
                      table.setItems(data);
            
                 }
            
                 public void gotoLogin(MouseEvent event) {
                      application.gotoLogin();
                 }
            
                 private void animateMessage() {
                      FadeTransition ft = new FadeTransition(Duration.millis(1000), success);
                      ft.setFromValue(0.0);
                      ft.setToValue(1);
                      ft.play();
                 }
            
                 public static class Person {
                      private final SimpleStringProperty firstName;
                      private final SimpleStringProperty lastName;
                      private final SimpleStringProperty email;
            
                      private Person(String fName, String lName, String email) {
                           this.firstName = new SimpleStringProperty(fName);
                           this.lastName = new SimpleStringProperty(lName);
                           this.email = new SimpleStringProperty(email);
                      }
            
                      public String getFirstName() {
                           return firstName.get();
                      }
            
                      public void setFirstName(String fName) {
                           firstName.set(fName);
                      }
            
                      public String getLastName() {
                           return lastName.get();
                      }
            
                      public void setLastName(String fName) {
                           lastName.set(fName);
                      }
            
                      public String getEmail() {
                           return email.get();
                      }
            
                      public void setEmail(String fName) {
                           email.set(fName);
                      }
            
                 }
            
                 @Override
                 protected void finalize() throws Throwable {
                      super.finalize();
                      log.debug("*** FINALIZE Profile***");
                 }
            
            }
            Login.fxml
            <?xml version="1.0" encoding="UTF-8"?>
            
            <?import java.lang.*?>
            <?import java.net.*?>
            <?import java.util.*?>
            <?import javafx.scene.control.*?>
            <?import javafx.scene.image.*?>
            <?import javafx.scene.layout.*?>
            <?import javafx.scene.paint.*?>
            
            <AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="500.0" styleClass="background" xmlns:fx="http://javafx.com/fxml" fx:controller="it.test.LoginController">
              <children>
                <AnchorPane id="anchorPane2" prefHeight="300.0" prefWidth="500.0" styleClass="top-segment" AnchorPane.bottomAnchor="200.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                  <children>
                    <VBox id="VBox" alignment="CENTER" prefHeight="300.0" prefWidth="421.0" spacing="5.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="29.0" AnchorPane.rightAnchor="40.0" AnchorPane.topAnchor="0.0">
                      <children>
                        <AnchorPane id="AnchorPane" prefHeight="180.0" prefWidth="430.0">
                          <children>
                            <Label id="label1" layoutX="14.0" layoutY="67.0" text="Username" />
                            <TextField id="textField1" fx:id="userId" layoutY="86.0" prefWidth="415.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="0.0" />
                            <Label id="label1" layoutX="14.0" layoutY="139.0" text="Password" />
                            <PasswordField id="passwordField1" fx:id="password" layoutY="157.0" prefWidth="415.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="0.0" />
                            <ImageView id="app-login-logo" layoutY="-12.0">
                              <image>
                                <Image url="@LoginLogo.png" preserveRatio="true" smooth="true" />
                              </image>
                            </ImageView>
                          </children>
                        </AnchorPane>
                      </children>
                    </VBox>
                  </children>
                </AnchorPane>
                <Button id="button1" fx:id="login" defaultButton="true" onAction="#processLogin" prefHeight="70.0" prefWidth="400.0" text="Login" AnchorPane.bottomAnchor="66.0" AnchorPane.leftAnchor="40.0" AnchorPane.rightAnchor="40.0" />
                <HBox id="hBox1" alignment="CENTER" prefHeight="25.0" prefWidth="418.0" AnchorPane.bottomAnchor="156.0" AnchorPane.leftAnchor="41.0" AnchorPane.rightAnchor="41.0">
                  <children>
                    <Label id="label2" fx:id="errorMessage" />
                  </children>
                </HBox>
              </children>
              <stylesheets>
                <URL value="@Login.css" />
              </stylesheets>
            </AnchorPane>
            Profile.fxml
            <?xml version="1.0" encoding="UTF-8"?>
            <?import java.lang.*?>
            <?import java.net.*?>
            <?import javafx.geometry.*?>
            <?import javafx.scene.control.*?>
            <?import javafx.scene.layout.*?>
            <?import javafx.scene.shape.*?>
            <?import javafx.scene.text.*?>
            
            <AnchorPane id="Profile" minWidth="800.0" prefHeight="800.0" prefWidth="500.0" styleClass="background" xmlns:fx="http://javafx.com/fxml" fx:controller="it.test.ProfileController">
              <children>
                <AnchorPane id="anchorPane1" prefHeight="371.0" prefWidth="500.0" styleClass="top-segment" AnchorPane.bottomAnchor="129.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                  <children>
                    <VBox id="VBox" alignment="CENTER" spacing="5.0" AnchorPane.bottomAnchor="32.5" AnchorPane.leftAnchor="42.0" AnchorPane.rightAnchor="40.0" AnchorPane.topAnchor="10.0">
                      <children>
                        <GridPane id="GridPane" hgap="15.0" vgap="15.0">
                          <children>
                            <Label id="Label" styleClass="profileTitle" text="Please review your profile." GridPane.columnIndex="0" GridPane.columnSpan="2147483647" GridPane.rowIndex="1">
                              <font>
                                <Font size="20.0" fx:id="x1" />
                              </font>
                            </Label>
                            <Label id="Label" font="$x1" text="User:" GridPane.columnIndex="0" GridPane.rowIndex="2">
                              <GridPane.margin>
                                <Insets top="2.0" fx:id="x3" />
                              </GridPane.margin>
                            </Label>
                            <TextField fx:id="user" editable="false" minWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
                            <Label id="Label" font="$x1" text="Email:" GridPane.columnIndex="0" GridPane.margin="$x3" GridPane.rowIndex="3" />
                            <TextField fx:id="email" minWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
                            <Label id="Label" font="$x1" text="Phone:" GridPane.columnIndex="0" GridPane.margin="$x3" GridPane.rowIndex="4" />
                            <TextField fx:id="phone" minWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="4" />
                            <Label id="Label" font="$x1" text="Address:" GridPane.columnIndex="0" GridPane.rowIndex="5" GridPane.valignment="TOP">
                              <GridPane.margin>
                                <Insets top="8.0" />
                              </GridPane.margin>
                            </Label>
                            <TextArea fx:id="address" maxHeight="82.0" minHeight="82.0" minWidth="200.0" prefHeight="82.0" GridPane.columnIndex="1" GridPane.rowIndex="5" />
                            <CheckBox id="" fx:id="subscribed" GridPane.columnIndex="1" GridPane.rowIndex="6">
                              <font>
                                <Font size="16.0" fx:id="x2" />
                              </font>
                              <GridPane.margin>
                                <Insets left="1.0" />
                              </GridPane.margin>
                            </CheckBox>
                            <Label id="Label" font="$x1" text="Subscribe to newsletter" GridPane.columnIndex="1" GridPane.rowIndex="6">
                              <GridPane.margin>
                                <Insets left="28.0" top="3.0" />
                              </GridPane.margin>
                            </Label>
                          </children>
                          <columnConstraints>
                            <ColumnConstraints hgrow="NEVER" minWidth="60.0" />
                            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
                          </columnConstraints>
                          <rowConstraints>
                            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
                            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
                            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
                            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
                            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
                            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
                          </rowConstraints>
                        </GridPane>
                        <TableView fx:id="table" onMouseClicked="#gotoLogin" prefHeight="252.5" prefWidth="718.0">
                          <columns>
                            <TableColumn prefWidth="75.0" text="Column 1" fx:id="column1" />
                            <TableColumn prefWidth="75.0" text="Column 2" fx:id="column2" />
                            <TableColumn prefWidth="75.0" text="Column 3" fx:id="column3" />
                          </columns>
                        </TableView>
                      </children>
                    </VBox>
                  </children>
                </AnchorPane>
                <HBox id="HBox" alignment="CENTER" spacing="5.0" AnchorPane.bottomAnchor="102.0" AnchorPane.leftAnchor="40.0" AnchorPane.rightAnchor="40.0">
                  <children>
                    <Label fx:id="success" font="$x2" opacity="0.0" text="Profile  successfully updated!" />
                  </children>
                </HBox>
                <HBox id="HBox" alignment="CENTER" spacing="20.0" AnchorPane.bottomAnchor="42.0" AnchorPane.leftAnchor="40.0" AnchorPane.rightAnchor="40.0" />
                <Button fx:id="save" defaultButton="true" font="$x1" layoutX="281.0" layoutY="687.0" maxHeight="2.147483647E9" onMouseClicked="#gotoLogin" prefHeight="50.0" prefWidth="205.0" text="Return to login" />
              </children>
              <stylesheets>
                <URL value="@Login.css" />
              </stylesheets>
            </AnchorPane>
            Thanks very much

            Edited by: drenda81 on 25-gen-2013 1.14
            • 18. Re: Heap space with Gui
              drenda81
              Hi,
              no one has tried my example?

              Thanks!
              1 2 Previous Next