I am a happy GWT and GXT user and have been using them building RIA for a while now. However, every once in a while, I have to embark on learning new technologies, venturing into uncharted territories simply because the project requirements demand it.  Once such uncharted territory (for me) was JavaFX. I had to do extensive image manipulation and I knew GWT would be a no go. And so, I started building with JavaFX (1.2), including the first few traditional web 2.0 UIs.

And I noticed something weird along the way. Anytime I change a node bound to the Scene by creating a new node instance, the previously abandoned Node was not getting garbage collected at all, even when there were no references to it in my application.

“That’s weird”, I thought. “I must be doing something wrong”. And I started paring down the UI until it was no more than a Stage, Scene and a custom node bound to Scene. And funny thing is that I saw the memory leak even then. Here is how my main code looks like:

var myCustomNode:MyCustomNode = MyCustomNode{}
var newNodeImage = Image {
  url: "{__DIR__}folder-new.png"
  width: 50
  height: 50
}
var newNodeImageView = ImageView {
  x: 10
  y: 10
  image: newNodeImage
  onMouseClicked: function(e: MouseEvent): Void {
    myCustomNode = MyCustomNode{ };
  }
}
var appBody = VBox { content: [newNodeImageView, myCustomNode] };
Stage {
  title: "Memory Leak Demo"
  width: 800
  height: 200
  scene: Scene { content: bind appBody }
}

The order of events is as follows:

  1. Everytime I click the ImageView, a new CustomNode instance is created.
  2. This custom node is indirectly bound to the scene. (In fact, as I understand it, this is the only way of switching scenes in JavaFX.) And by the way, my Custom Node looks like this:
public class MyCustomNode extends CustomNode {

  //bunch of UI components defined here. A few text boxes, radio buttons nothing fancy

  override function create() {
    VBox {
      content: [
        HBox { content: [ ....] }
        HBox { content: [ ....] }
        ....
      ]
    }

}

}

As you can see, it is a pretty simple node. Nothing fancy.

Here is the code for you to grab and try it out yourself: Main.fx MyCustomNode.fx

When I ran this, I monitored using JConsole. Here is what I saw:

Memory status in JConsole

Memory status in JConsole

The JConsole screenshot showing the memory usage is self explanatory.

  1. Every time I created a new CustomNode, the memory usage went up
  2. Even when it reached the critical point, full GC never occurred to recollect all orphaned nodes.
  3. Fair enoguh, to say that there is are loitering objects that never get garbage collected, as if somebody is holding on to them.
  4. I believe it the JavaFX engine itself that builds complex internal graphs and holds on to previously bound variables. (I am not sure how I can prove it though)

Then I did something interesting.

I removed all radio buttons and all instance variables from MyCustomNode.fx and retained only Texts (a whole lot of them, but only Texts) and ran the tests again.

Surprise Surprise! No Memory leaks this time.

This is the memory usage I saw:

Memory monitored in JConsole when CustomNode only had Text

Memory monitored in JConsole when CustomNode only had Text

As you can see, when I used a Node with only Text{}, there was no memory leaks. The moment I introduced radio buttons into the node and made them instance variables, I began to see memory leaks.

There is always a chance that I might be doing something wrong, but seems unlikely since this is a simple example and almost all examples I saw for switching scene involved binding.

Moral of the story:

  1. All switched scenes should be created once and retained in memory for the duration of the application.
  2. All data populated into the scenes could be recycled but not the underlying nodes that make up the scenes

That seems very limiting for a new platform that aspires to be the ruler of Web 2.0 and Flash and Silverlight killer.

Here is the code for you to grab and try it out yourself: Main.fx MyCustomNode.fx


Cheers,

Srikanth Shenoy