This discussion is archived
1 2 Previous Next 18 Replies Latest reply: Jan 29, 2013 9:46 AM by drenda81 RSS

Heap space with Gui

drenda81 Newbie
Currently Being Moderated
Hi guys,
I've a big problem with JavaFx and heap space. I've a small test case in order to reproduce the problem. I have a scene in with each time a clear all widget and then I add a Panel. Making several times this operation, at the end it goes to heap space.
I thought that the gc would clean the memory. Maybe I'm making some mistake?

Thanks!

     try {

               final Group root = new Group();
               Scene s = new Scene(root, 800,600);
               Button button = new Button();
               button.setLayoutX(100);
               button.setLayoutY(80);
               button.setText("Action");

               button.setOnAction(new EventHandler<ActionEvent>() {
                    
                    @Override
                    public void handle(ActionEvent arg0) {
                         for (int i = 0; i < 1000; i++) {
                              root.getChildren().clear();
                              AnchorPane pane = new AnchorPane();
                              pane.setPrefWidth(600);
                              pane.setPrefHeight(800);
                              pane.getChildren().clear();
                              root.getChildren().add(pane);
                              for (int x = 0; x < 500; x++) {
                                   pane.getChildren().add(new TextField("Text "+x));
                              }
                              System.out.println("i: "+i);
                         }
                    }
               });
               root.getChildren().add(button);

               primaryStage.setScene(s);
               primaryStage.show();

               
               if(true)return;

} catch (Throwable ex) {
               ex.printStackTrace();
          }
  • 1. Re: Heap space with Gui
    drenda81 Newbie
    Currently Being Moderated
    Hi guys,
    any ideas about my problem?

    Thanks
  • 2. Re: Heap space with Gui
    MiPa Pro
    Currently Being Moderated
    You'd probably get more feedback if you'd provide a complete working example and not just a code snippet.
    Michael
  • 3. Re: Heap space with Gui
    drenda81 Newbie
    Currently Being Moderated
    Hi Mipa,
    the problem is reproducible with the code I pasted.
    Paste all my code is difficult and maybe would be unclear understanding the problem. Also in the simple code I pasted I believe that there is something wrong because memory always increase.

    Thanks for your reply
  • 4. Re: Heap space with Gui
    James_D Guru
    Currently Being Moderated
    drenda81 wrote:
    the problem is reproducible with the code I pasted.
    Paste all my code is difficult and maybe would be unclear understanding the problem.
    It's still a lot easier if you include a complete, executable example. To run your code I had to create a new (FX main) class, copy and paste your code into the start(...) method, and then fix all the imports, one at a time. It's also much easier to read the code if you follow the instructions in "How to post code" (the sticky thread near the top of the forum).

    This did run out of memory when I ran it, but it may just be that the FX application thread doesn't have the opportunity to release any resources since you're hogging that thread. I tried a version where I delegated the work to a different thread and updated the UI using Platform.runLater(...); in that case it ran out of memory unless I put a Thread.sleep(...) in there. with the Thread.sleep(...) the garbage collector appeared to run fairly frequently and it ran fine. So I think in the case with a separate thread but without a pause it's simply flooding the FX Application Thread with too many Runnables too quickly. This probably shouldn't happen, even in that case, and if you change the TextFields to Labels it seems to run fine.

    This isn't a very realistic example; you're creating hundreds of controls, adding them to the scene graph, and immediately replacing them. You would obviously never do this in real life. Any attempt to make it look realistic (like pausing for even just a few milliseconds between each update) makes the problem go away. Do you have a realistic example that fails?

    Here's the code I had after experimenting a bit. I just threw the threading together without thinking about it much, so you shouldn't use this as an example of how to write threaded FX apps (though I think it's correct); or indeed as an example of how to do memory profiling.
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.concurrent.Task;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Control;
    import javafx.scene.control.Label;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    import javafx.stage.WindowEvent;
    
    public class HeapSpaceTest extends Application {
    
      @Override
      public void start(Stage primaryStage) {
        try {
    
          final BorderPane root = new BorderPane();
          Scene s = new Scene(root, 800, 600);
          Button button = new Button();
          button.setText("Action");
    
          final ExecutorService service = Executors.newSingleThreadExecutor();
    
          button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent arg0) {
              final Task<Void> task = new CreateNodeTask(root);
              service.submit(task);
            }
          });
    
          root.setTop(button);
    
          final Label freeMemLabel = new Label();
    
          final Task<Void> memMonitor = new Task<Void>() {
            @Override
            public Void call() {
              while (!isCancelled()) {
                try {
                  Thread.sleep(500);
                } catch (InterruptedException exc) {
                  if (isCancelled()) {
                    break;
                  }
                }
                updateMessage(memInfo());
              }
              return null;
            }
          };
          freeMemLabel.textProperty().bind(memMonitor.messageProperty());
          root.setBottom(freeMemLabel);
          final ExecutorService memMonitorService = Executors.newSingleThreadExecutor();
          memMonitorService.submit(memMonitor);
    
          primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
              memMonitor.cancel(true);
              memMonitorService.shutdown();
              service.shutdown();
            }
          });
    
          primaryStage.setScene(s);
          primaryStage.show();
    
        } catch (Throwable ex) {
          ex.printStackTrace();
        }
      }
    
      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 Free: %d Used: %d", max, total, free, used);
      }
    
      private class CreateNodeTask extends Task<Void> {
        private final BorderPane container;
    
        CreateNodeTask(BorderPane container) {
          this.container = container;
        }
    
        @Override
        public Void call() {
          for (int i = 0; i < 1000; i++) {
            try {
              Thread.sleep(5);
            } catch (InterruptedException exc) {
              if (isCancelled()) {
                break;
              }
            }
            final VBox box = new VBox();
            box.setPrefWidth(600);
            box.setPrefHeight(800);
            box.getChildren().clear();
            for (int x = 0; x < 500; x++) {
              final TextField textField = new TextField("Text " + x);
              textField.setMinHeight(Control.USE_PREF_SIZE);
              box.getChildren().add(textField);
              // pane.getChildren().add(new Label("Text " + x));
            }
            System.out.println("i: " + i + " " + memInfo());
            final ScrollPane scroller = new ScrollPane();
            scroller.setContent(box);
            Platform.runLater(new Runnable() {
              @Override
              public void run() {
                container.setCenter(scroller);
              }
            });
          }
          return null;
        }
      }
    
      public static void main(String[] args) {
        launch(args);
      }
    }
  • 5. Re: Heap space with Gui
    drenda81 Newbie
    Currently Being Moderated
    Thanks very much for you time. It 's true the example is not realistic but it was to understand how Javafx's graph works.
    We have a main screen where there are buttons that open different screens replacing the contents of the main gui.
    My problem is that as you use the program memory always grows until Heap space. Every time I press a button I get the Pane (container) of the main gui, I clear it and then I add a Node that is the new screen I want to display.

    I don't know if the method clear() remove all references to the old screen and it free memory. Seems that this don't work in this manner. So which is the correct way to implements a transit from a screen to another?

    Thanks very much
  • 6. Re: Heap space with Gui
    James_D Guru
    Currently Being Moderated
    The first thing to point out here is that looking at memory usage doesn't really tell you much about what the JavaFX API is doing, because memory usage depends on what the garbage collector is doing. The API can free up references all it likes, but the memory usage won't change until the JVM determines that the gc needs to be run. (Most, if not all, controls you create will be long-lived enough to undergo generational promotion, so the memory they consume will only be reclaimed on a full collection.)

    That said, we can try and make guesses as to what is going on. In particular, if you see the garbage collector freeing up memory, it seems reasonable to assume that there are objects to which there are no live references.

    If you run my code, you'll see that the memory use generally increases, with fairly large drops in the memory usage from time to time. The drops in usage correspond (I assume) to the garbage collector being invoked by the JVM and deallocating memory for objects to which there is no live reference. On my system, the first such run of the garbage collector happens on iteration 223 of the outer loop. (I should point out here that the gc is almost certainly not sitting idle until this point, but this probably corresponds to the first time it does a full collection.) This corresponds to the point in the app where over 110,000 text fields have been created and abandoned.

    What we learn (or at least guess) from this is:
    1. JavaFX is releasing references and making the objects to which they point eligible for garbage collection.
    2. You are very unlikely to see this actually happen in the lifetime of a real desktop application, (perhaps unless you are displaying tables of large quantities of data). Simply creating one view and then another a few times is not likely to consume enough memory to cause the gc to do a full collection.

    What is really going on behind the scenes is likely to be pretty complicated, and you really don't want to know about it (in the sense that you don't want to write code that assumes a certain model). Clearing the children from a parent's list of children should remove all the references to those nodes (as long as you are not retaining them in your code); and the test code here confirms that happens. It does seem that creating vast quantities of controls within a single rendering frame (or a few frames) causes references to those nodes to be retained for a short while, but to be honest if you are creating thousands of controls within a few frames you're doing something wrong.

    Is your app running out of memory? If so, you should probably suspect the data structures you are using rather than the controls you are creating. If not, then why are you worried about it?

    Edited by: James_D on Jan 23, 2013 6:02 AM
  • 7. Re: Heap space with Gui
    drenda81 Newbie
    Currently Being Moderated
    Hi,
    thanks a lot. Yes I've a problem in my javafx application with a normal use i reach heap space in a short time.
    I create a small test case application that reproduce faithfully my real application. I have 2 view with related controller and a Main App. This is the link of the eclipse project: http://5.135.176.104/test.zip
    If you run the application and then switch from one view to other (the menu is on the left), after about 30 transitions it goes to heap space. You can also wait several minutes from one switch to other but the problem is alway present.

    I don't see any reference to my view that can prevent the gb to destroy the nodes. Another strange thing that I noticed is that my controllers is never finalized().

    Thanks very much
  • 8. Re: Heap space with Gui
    James_D Guru
    Currently Being Moderated
    Yup, it does run out of memory...

    I'm certainly not going to go through all your code and try to figure out what's going wrong. My instinct would be to restructure this considerably, though. You have two different screens; each time you want to switch between them you are reloading them from scratch from the FXML file and recreating everything. Wouldn't it make more sense to load the two screens at startup and just switch between them? Clear out all the data in between each if you need, or as the logic dictates. This approach should perform a lot better even if there are no memory issues.

    Nice looking app, by the way. The calendar controls are very nice.
  • 9. Re: Heap space with Gui
    drenda81 Newbie
    Currently Being Moderated
    Thanks James. You're right, I could load the fxml at once. However, the screens are quite complex, so I would not make too slow application at startup. For this reason I load the Fxml only when that view is required.

    Do you think the loading dell'fxml every time the view is drawn can cause the heap space? Although I load the views from fxml at startup, however I would have the same problem because I have about 30/40 different screens.
    In the test case heap space happens after relatively few steps 20-30. For this reason, in theory, I should have the same problem even if the preload fxml at startup.Do you agree?

    So my question would be: what is the correct way to switch from one view to another, loaded from fxml, also using the controller?

    The ensamble sample doesn't use fxml but every Page has a method that create programatically the view.

    I can not understand if the problem is the complexity of my screens and the fact that I load taht from fxml, or if I'm doing something wrong at the conceptual level.

    thank you very much
  • 10. Re: Heap space with Gui
    James_D Guru
    Currently Being Moderated
    drenda81 wrote:
    Thanks James. You're right, I could load the fxml at once. However, the screens are quite complex, so I would not make too slow application at startup. For this reason I load the Fxml only when that view is required.
    That's a design decision... I tend to prefer the slow startup and a nice, fast, responsive app once it's running. But I understand the other point of view.
    Do you think the loading dell'fxml every time the view is drawn can cause the heap space? Although I load the views from fxml at startup, however I would have the same problem because I have about 30/40 different screens.
    In the test case heap space happens after relatively few steps 20-30. For this reason, in theory, I should have the same problem even if the preload fxml at startup.Do you agree?
    Possibly... On my machine it actually didn't fail until over 100 screen changes. It really shouldn't be happening anyway, so, you kind of need to fix the problem independently of that design decision.
    So my question would be: what is the correct way to switch from one view to another, loaded from fxml, also using the controller?
    Yes, sure. You can have a "master" fxml file which includes fxml files for all the screens; inject the screens into the controller in the usual way and then use the controller to switch between them in response to user events. See fx:include and fx:define, and probably the section on nested controllers in http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html if you're not familiar with that.
    The ensamble sample doesn't use fxml but every Page has a method that create programatically the view.
    Building the screens programmatically will likely be somewhat faster than using fxml; no file loading or parsing, and everything will be created using constructor calls rather than via reflection (the last is a minor point). But it shouldn't make any significant difference to memory use.
    I can not understand if the problem is the complexity of my screens and the fact that I load taht from fxml, or if I'm doing something wrong at the conceptual level.
    Probably your best bet is to build something relatively simple: a couple of screens loaded "on demand" from fxml with similar controller structure to what you have. (Your controller structure looked a little weird to me, with the static fields in the controllers.) Then test that out and see how it does. If it doesn't show the same memory issues, then add complexity until you replicate the problem (or you end up with a fully functional app!).

    You might want to look into finding a memory profiling tool, to see if you can figure out what's getting retained. I don't have any experience with those so can't recommend anything.
  • 11. Re: Heap space with Gui
    jsmith Guru
    Currently Being Moderated
    The ensamble sample doesn't use fxml but every Page has a method that create programatically the view.
    I think initial development of ensemble occurred before the fxml system was solidified - were it written from scratch today, perhaps the designers would choose to use fxml instead.
    Building the screens programmatically will likely be somewhat faster than using fxml; no file loading or parsing, and everything will be created using constructor calls rather than via reflection (the last is a minor point). But it shouldn't make any significant difference to memory use.
    Logically, for most practical purposes, it not really have a noticable difference for UI speed and responsiveness either, especially if the fxml resources are being sourced from a local jar file rather than over a network - as a comparison, on a modern PC a browser can parse and display a complex local html file almost instantaneously from a user viewpoint - javafx performance for loading and displaying complex fxml should be at least at fast.
    You might want to look into finding a memory profiling tool, to see if you can figure out what's getting retained. I don't have any experience with those so can't recommend anything.
    For something free use jvisualvm which comes with the jvm.
    http://docs.oracle.com/javase/7/docs/technotes/tools/share/jvisualvm.html
    For something paid use jprofiler (it has free trial and is a really great tool.
    http://www.ej-technologies.com/products/jprofiler/overview.html
    JVM memory profiling is both an art and a science . . .
  • 12. Re: Heap space with Gui
    drenda81 Newbie
    Currently Being Moderated
    Thanks very much James. I make several tests with profiler and I found thow problems:

    -In my test case I have a ChangeListener on widthProperty and heightProperty on my superclass controller and every time it is added to controller.So every time is added a listener to the view --> WRONG!! So I resolved this problem. So this is a my error.
    -Another thing that I noticed is this: when I load the fxml I see that objects com.sun.javafx.css.CascadingStyle always grow!!! In fact in my fxml I have the reference to my css:
    <stylesheets>
    <URL value="@../../../Optix.css" />
    </stylesheets>
    Every time that I load the fxml these Objects grow and never fall!! So I make a test and I removed the reference to css in my fxml file and all works fine!! My test application use 16-30Mb of memory! Great!!

    I've only a dubt: Do you think it is normal that the CascadingStyle objects grow more when I load a new fxml? Maybe it's a bug? We said that it is a choice to decide to load the fxml before loading the application, or when viewing screens.
    If this is true, the objects of css should not grow when I load the fxml. Do you agree?

    Of course in my stage I can add the stylesheet:
    scene.getStylesheets().add("it/test2/ui/style.css");

    but I add the stylesheet in my fxml file so I can see the real aspect when I modfy it with Scene Builder.

    Thanks very much for your support!
  • 13. Re: Heap space with Gui
    James_D Guru
    Currently Being Moderated
    If you add the stylesheet in your Java code (rather than in FXML), you should add it to the root element loaded from the FXML (the Region in your code). If you add it to the Scene, the stylesheets will (or at least could) be retained by the scene each time an fxml file is loaded.

    That said, I would think that defining the stylesheet in the FXML as you have would make the stylesheet a property of the root node of the FXML anyway. Once you replace that node, any css-related objects should be out of scope. So this maty be a bug.

    Does your profiler tell you if the CascadingStyle objects are referenced directly from the Scene, or only from your AnchorPanes?
  • 14. Re: Heap space with Gui
    James_D Guru
    Currently Being Moderated
    jsmith wrote:

    Logically, for most practical purposes, it not really have a noticable difference for UI speed and responsiveness either, especially if the fxml resources are being sourced from a local jar file rather than over a network - as a comparison, on a modern PC a browser can parse and display a complex local html file almost instantaneously from a user viewpoint - javafx performance for loading and displaying complex fxml should be at least at fast.
    This is, of course, absolutely correct. I spend so much of my life writing handlers that process huge files (and that have to be done in the background) that I've developed a knee-jerk reaction to event handlers that do any kind of IO processing. But jsmith is right; this would likely complete within a single rendering frame and would be effectively instantaneous to the user.
1 2 Previous Next

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points