24

I haven't found a simple answer for these two questions:

  1. do I have to remove a listener before deleting the property instance (the listener is not used anywhere else)?

    BooleanProperty bool = new SimpleBooleanProperty();
    bool.addListener(myListener);
    bool.removeListener(myListener); // is it necessary to do this?
    bool = null;
    
  2. do I have to unbind a uni-directional bounded property before deleting the property instance?

    BooleanProperty bool = new SimpleBooleanProperty();
    bool.bind(otherBool);
    bool.unbind(); // is it necessary to do this?
    bool = null;
    
Neuron
  • 5,141
  • 5
  • 38
  • 59
dpelisek
  • 884
  • 3
  • 13
  • 22
  • see similar discussion here : https://forums.oracle.com/forums/thread.jspa?threadID=2394063&start=0&tstart=0 – invariant Jan 28 '13 at 16:13
  • 1
    Invariant, that was the first result when I put the key words into google. Be sure that I would not ask if the answer was really there :) – dpelisek Jan 29 '13 at 10:19

2 Answers2

32

Case 1

Given that myListener "is not used anywhere else" and therefore I assume, a [method-] local variable, the answer is no. In the general case though, the answer is mostly a no but can sometimes be a yes.

As long as myListener is strongly reachable, then it will never become eligible for finalization and it will continue to consume memory. For example, this would be the case if myListener is a "normally" declared static variable (all "normal" references in Java are strong references). However, if myListener is a local variable, then the object will not be reachable anymore after the return of the current method call and bool.removeListener(myListener) is a bit meaningless over-engineering. Both the observer and the Observable goes out of scope and will eventually be finalized. A quote from my own blog post about this answer might paint a better picture:

It doesn’t matter if the box know about the cat inside of it, if you throw the box into the ocean. If the box isn't reachable, nor is the cat.

Theory

To fully understand the situation here, we have to remind ourselves of the life-cycle of a Java object (source):

An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it. [..] An object is weakly reachable if it is [not] strongly [..] reachable but can be reached by traversing a weak reference. When the weak references to a weakly-reachable object are cleared, the object becomes eligible for finalization.

In the case of static variables, these will always be accessible as long as the class is loaded, thus reachable. If we didn't want a static reference to be the one that hinder the garbage collector to do his job, then we could declare the variable to use a WeakReference instead. JavaDoc says:

Weak reference objects [..] do not prevent their referents from being made finalizable, finalized, and then reclaimed. [..] Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object [..]. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable.

Explicit management

For illustration, let's assume that we write a JavaFX space simulation game. Whenever an Observable planet moves into the view of a spaceship observer, the game engine register the spaceship with the planet. It is quite apparent that whenever the planet goes out of view, the game engine should also remove the spaceship as an observer of the planet by using Observable.removeListener(). Otherwise, as the spaceship continues to fly through space, memory will leak. Eventually, the game cannot handle five billion observed planets and it will crash with an OutOfMemoryError.

Do note that for the vast majority of JavaFX listeners and event handlers, their life-cycle is parallel to that of their Observable so the application developer has nothing to worry about. For example, we might construct a TextField and register with the text field's textProperty a listener that validate user input. As long as the text field sticks around, we want the listener to stick around. Sooner or later, the text field is not used anymore and when he is garbage collected, the validation listener is also garbage collected.

Automatic management

To continue on the space simulation example, assume that our game has limited multiplayer support and all the players need to observe each other. Perhaps each player keep a local score board of kill metrics or perhaps they need to observe broadcasted chat messages. The reason is not the important point here. What would happen when a player quit the game? Clearly, if the listeners are not explicitly managed (removed), then the player who quit will not become eligible for finalization. The other player's will keep a strong reference to the offline player. Explicit removal of the listeners would still be a valid option and probably the most preferred choice for our game, but let's say that it feels a bit obtrusive and we want to find a more slick solution.

We know that the game engine keep strong references to all players online, for as long as they are online. So we want the spaceships to listen for changes or events of each other only for as long as the game engine keep the strong references. If you read the "theory" section, then surely a WeakReference sounds like a solution.

However, just wrapping something in a WeakReference is not the entire solution. It seldom is. It is true that when the last strong reference to the "referent" is set to null or otherwise become unreachable, the referent will be eligible for garbage collection (assuming that the referent cannot be reached using a SoftReference). But the WeakReference is still hanging around. The application developer need to add some plumbing so that the WeakReference itself is removed from the data structure he was put in. If not, then we might have reduced the severity of the memory leak but a memory leak will still be present because dynamically added weak references consume memory too.

Lucky for us, JavaFX added interface WeakListener and class WeakEventHandler as a mechanism for "automatic removal". The constructors of all related classes accept the real listener/handler as provided by client code, but they store the listener/handler using a weak reference.

If you look at the JavaDoc of WeakEventHandler, you'll notice that the class implement EventHandler, so the WeakEventHandler can be used wherever an EventHandler is expected. Likewise, a known implementation of a WeakListener can be used wherever an InvalidationListener or a ChangeListener is expected.

If you look into the source code of WeakEventHandler, you'll notice that the class is basically only a wrapper. When his referent (the real event handler) is garbage collected, the WeakEventHandler "stop working" by not doing anything at all when WeakEventHandler.handle() is called. The WeakEventHandler doesn't know about which object he has been hooked up with, and even if he did, the removal of an event handler is not homogeneous. All known implementing classes of WeakListener has a competitive advantage though. When their callbacks are invoked, they are implicitly or explicitly provided a reference to the Observable they are registered with. So when the referent of a WeakListener is garbage collected, eventually the WeakListener implementation will make sure that the WeakListener itself is removed from the Observable.

If it is isn't already clear, the solution for our space simulation game would be to let the game engine use strong references to all online spaceships. When a spaceship goes online, all other online spaceships are registered with the new player using a weak listener such as WeakInvalidationListener. When a player goes offline, the game engine remove his strong reference to the player and the player will become eligible for garbage collection. The game engine doesn't have to bother about explicit removal of the offline player as a listener of the other players.

Case 2

No. To better understand what I'll say next, please read my case 1 answer first.

BooleanPropertyBase store a strong reference to otherBool. This in itself does not cause otherBool to always be reachable and thus potentially cause a memory leak. When bool becomes unreachable, then so do all its stored references (assuming they are not stored anywhere else).

BooleanPropertyBase also works by adding itself as an Observer of the property you bind it to. However, it does so by wrapping itself in a class that works almost exactly like the WeakListeners described in my case 1 answer. So once you nullify bool, it will be only a matter of time before it is removed from otherBool.

bruno
  • 2,213
  • 1
  • 19
  • 31
Martin Andersson
  • 18,072
  • 9
  • 87
  • 115
  • 4
    To tell the truth I don't understand this answer. Case 1 - no or yes. Case 2 - no, see case 1. – Pavel_K Sep 03 '17 at 11:38
  • 1
    The answer for case 2 is a "no". The back reference to case 1 was put there only for new readers scrolling directly to case 2 as a hint that they should read case 1 first to better understand the explanation in case 2. This stuff is complicated by nature and I hate that I haven't been able to simplify the text more. At the same time, I feel the topic deserve to be written using a technically correct language that covers all aspects, because otherwise, leaving gaps will only provoke new questions rendering the reader even more insecure and confused. – Martin Andersson Sep 05 '17 at 19:42
  • I see. Thank you for your explanation. – Pavel_K Sep 05 '17 at 19:44
  • I believe it's necessary for most of us to reread an answer like this a couple of times in order to understand it, and probably do extensive googling on the side. Hopefully, you can make sense out of the answer but please feel free to edit it if you find that improvements can be made. Simplification is always an improvement! =) Also, please let me know if you just can't find a way to understand it. If so, I will try to come up with an alternative. – Martin Andersson Sep 05 '17 at 19:50
  • 1
    Thank you very much for you wish to help. I think it is one of the most important quality of a man - wish to help someone. After reading it sometime ago I came to conclusion that situation with properties listeners and bindings (particularly with bidirectional bindings) is not so clear. Fortunately I use MVVM pattern, and I have view and viewmodel in component that are tight coupled with their properties. However, model doesn't have any fx properties, so when I destroy my component I don't care if view is still linked to viewmodel properties or not. I destroy both of them. – Pavel_K Sep 05 '17 at 20:08
  • Does this apply to custom bindings as well? E.g, If I create a binding with Bindings.createBooleanBinding(), there is no need to unbind dependencies before the binding is garbage collected, right? – Vladimir M. Feb 21 '18 at 08:55
8

I completely agree with the case 1 answer, but the case 2 is a bit more tricky. The bool.unbind() call is necessary. If ommitted, it does cause a small memory leak.

If you run the following loop, the application will eventually run out of memory.

BooleanProperty p1 = new SimpleBooleanProperty();
while(true) {
    BooleanProperty p2 = new SimpleBooleanProperty();
    p2.bind(p1)
}

The BooleanPropertyBase, intenally, does not use a real WeakListener (an implementation of the WeakListener interface), it is using a half-baked solution. All the "p2" instances get eventually garbage-collected, but a listener holding an empty WeakReference remains in the memory forever for each "p2". The same holds for all properties, not only BooleanPropertyBase. It's explained here in detail, and they say it is fixed in Java 9.

In most cases, you do not notice this memory leak, because it leaves only a few dozen bytes for every binding that has not been unbound. But in some cases it caused me real trouble. An good example are table cells of a table that gets frequently updated. The cells then re-bind to different properties all the time, and these left-overs in the memory accumulate quickly.

Neuron
  • 5,141
  • 5
  • 38
  • 59
Jan X Marek
  • 2,464
  • 2
  • 18
  • 26