Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the recommended approach to keeping intermediate bindings from being garbage collected in JavaFX 8

TL;DR: GC is eating my active bindings.

I have an app that was developed and successfully deployed using JavaFX 2.2 on Java 7.

When I upgraded/transitioned to JavaFX 8.0 (and Java 8), certain features would "mysteriously" stop working--mid application lifecycle--with no exceptions or other indications of erroneous state change. For example; buttons stop working, custom cell renderers stopped being applied, enabled/disabled state stopped getting updated.

After many hours of digging, I think I've tracked the problem down to what I understand to be some changes in JavaFX 8 and internal usage of javafx.beans.WeakListener to deal with memory leaks in found JavaFX 2.2. Basically, it seems that the bindings that I'm creating to manage data state dependency are getting garbage collected despite the fact that the Nodes they control are still active.

The problems most often seem to arise when I instantiate bindings using anonymous classes. Some but not all of my problems can be fixed by storing a reference to the binding as a class member, thereby keeping the GC from collecting it. I've even had whole controllers get GC'd because they were instantiated via FXML loading and never directly referenced (I now always stuff a reference to the controller in the parent node's userData property).

My problems are:

  1. associated bugs arise non-deterministically (or are at least a function of memory footprint,
  2. if bindings though anonymous classes should be avoided, it's a lot of work to find every instance in a large, existing code base to change it
  3. even if I could find every instance, it clutters up the code immensely

Frustratingly, I can't seem to find anything in the Oracle documentation that says "don't create bindings with anonymous classes", or any other guidelines for ensuring reliable use of bindings. A lot of the code examples out there use anonymous class bindings. Nor can I find any notes on how to properly update a JavaFX 2.2 app to JavaFX 8.

Any advice from people developing non-trivial JavaFX apps is greatly appreciated (I've been developing JavaFX 2.x apps for 3 years, and Swing apps for > 15 years, so this isn't exactly a n00b question).


Note: My question is similar to Clean JavaFX property listeners and bindings (memory leaks), but I want to know specifically and definitively how to use complex bindings and ensure they won't be garbage collected at random times, without resorting to polluting classes with references to every instance.

like image 344
metasim Avatar asked Jan 29 '15 13:01

metasim


People also ask

How is Java's garbage collection implemented?

Java garbage collection is an automatic process. The programmer does not need to explicitly mark objects to be deleted. The garbage collection implementation lives in the JVM. Each JVM can implement garbage collection however it pleases; the only requirement is that it meets the JVM specification.

What is garbage collection in memory management?

Garbage collection (GC) is a memory recovery feature built into programming languages such as C# and Java. A GC-enabled programming language includes one or more garbage collectors (GC engines) that automatically free up memory space that has been allocated to objects no longer needed by the program.

How does garbage collector free memory?

When the garbage collector performs a collection, it releases the memory for objects that are no longer being used by the application. It determines which objects are no longer being used by examining the application's roots.

What are binding properties used for in JavaFX?

JavaFX properties are often used in conjunction with binding, a powerful mechanism for expressing direct relationships between variables. When objects participate in bindings, changes made to one object will automatically be reflected in another object. This can be useful in a variety of applications.


1 Answers

The WeakEventHandler is -supposed- to allow GC of the listener object (if it is not otherwise referenced) and simply stop working at that time. As you have discovered, this means you have to reference the handler as long as you need it to keep triggering. This requirement is more or less independent of whether you use an anonymous class or not; it would fail in the same way if you used a normal class.

There is no possible way to "automatically" determine that a certain event will not be triggered in the future anymore, which is essentially what a feature request to "fix" this problem would require. If you don't want anything GC'd you can simply add all the anonymous listeners to a list stored as a static variable somewhere. If you want GC to work (and eventually you will), you will have to control it by maintaining references only when they are needed and releasing them when no longer.

like image 85
Atsby Avatar answered Sep 30 '22 07:09

Atsby