Run the program in the first comment - the memory gets GCed correctly.
Run the program in the second comment - the memory grows forever.
The reason is that many listeners used internally in JFX bindings hold a weak reference to the target listener, but do not implement the javafx.beans.WeakListener interface.
Hence they are never removed within the ExpressionHelperBase.trim() method, even though the target listener had been GC-ed.
In this case, it is the StringPropertyBase$Listener class, but it happens on many more places across JavaFX.
The posted example is actually very realistic. In our application, short-lived objects or table cells used to bind to long-lived properties (imagine, for example, order status, which often does not change for days, but when it changes, we need to display the new value).
After a couple of hours of extensive work with the application (lots of table updates, lots of scrolling), we sometimes had a couple hundreds MB occupied by these empty listeners. Not only that it consumes memory, the application gets overall slower, as the properties have to scroll through the long lists of listeners.
One simply can not use bindings in such cases and must register explicit weak listeners.
I believe that the issue could be fixed simply by implementing the WeakListener interface in those listeners used in bindings, wouldn't it?