On Mon, 3 Oct 2022 23:07:08 GMT, Andy Goryachev <ango...@openjdk.org> wrote:

>>> You are right, @kleopatra - this executeOnceWhenPropertyIsNonNull() is 
>>> installing a listener. perhaps we should add a similar functionality to 
>>> LambdaMultiplePropertyChangeListenerHandler and actually install a 
>>> WeakListener instead.
>> 
>> I'd advise against doing this in an ad-hoc manner. Skins leak memory by 
>> adding (but not removing) all kinds of listeners or children to their 
>> controls. Some skins try to address this with weak listeners, or by manually 
>> removing listeners in the dispose() method. SkinBase attempts to make it 
>> easier for skins to not leak listeners with the 
>> `registerInvalidationListener` and `registerChangeListener` APIs, which 
>> remove an added listener when the skin is disposed (however, both of these 
>> methods only accept Consumers, not actual 
>> `InvalidationListener`/`ChangeListener` instances, making the API less 
>> useful than it could be).
>> 
>> But there are more places where memory is leaked:
>> 1. adding, but not removing children to the control
>> 2. adding EventHandlers/EventFilters to the control or to other objects (for 
>> example, MenuBarSkin adds EventHandlers to the control's scene)
>> 3. hidden listeners like the one you discovered: 
>> Utils.executeOnceWhenPropertyIsNonNull
>> 4. Label.setLabeLFor(control)
>> 5. ListChangeListener, etc.
>> 
>> Most skins exhibit one or several of these memory leaks, which causes them 
>> to remain strongly referenced by the control even after being disposed. The 
>> problem here is that JavaFX doesn't offer good tools and APIs to address 
>> these challenges, so it's incredible easy to create a skin that leaks 
>> memory. In fact, it's so easy that most built-in skins do it (and these were 
>> created by the developers of JavaFX itself, so obviously it's a major 
>> problem if even those people get it wrong so often).
>> 
>> For me, this is a clear indication that we're dealing with an API problem 
>> first and foremost. I think there should be a better API that makes it easy 
>> for skins to interact with their associated controls, instead of hand-wiring 
>> hundreds of little skin-control dependencies (and sure enough, getting it 
>> wrong every so often).
>
> You are absolutely right!  There are so many places where memory leaks are 
> being created.  My plan is to go through all the skins and take care of them 
> all.
> 
> LambdaMultiplePropertyChangeListenerHandler is, I believe, a step in the 
> right direction.  I wish there were a more uniform and public way to add 
> various listeners, implemented as a public class - something that can be used 
> to yank all listeners with a single method.
> 
> I've done something like that on the application level, see 
> https://github.com/andy-goryachev/FxTextEditor/blob/master/src/goryachev/fx/FxDisconnector.java
> 
> What we could do (later) is to keep enhancing 
> LambdaMultiplePropertyChangeListenerHandler by adding more ways of adding 
> listeners, including full-blown change/invalidation listeners, and then 
> rename it, move to a public util package and make it a part of the general 
> purpose API.

Sure, but I think this should be done with a clear vision of where we're going, 
and not just as a collection of ad-hoc helper methods. There are even more 
problems that a new API should at least consider:
Some skins change their behavior depending on the configuration of their 
associated control at the time when the skin is installed. For example, 
`MenuButtonSkinBase` adds a `MOUSE_PRESSED` handler only if the control didn't 
specify one for its onMousePressed property:

    if (control.getOnMousePressed() == null) {
        control.addEventHandler(MouseEvent.MOUSE_PRESSED, ...

But what happens if the `onMousePressed` property is set after the skin is 
installed?
Rather than a one-time check, cases like these should probably be a dynamic 
expression:

    when control.onMousePressedProperty().isNotNull() and skin.isInstalled()
        control.addEventHandler
    otherwise
        control.removeEventHandler

-------------

PR: https://git.openjdk.org/jfx/pull/906

Reply via email to