javajavafxjava-8javafx-8

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 found in 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 through 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.


Solution

  • 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.