RFR: 8294589: MenuBarSkin: memory leak when changing skin [v15]

John Hendrikx jhendrikx at openjdk.org
Wed Nov 30 21:46:42 UTC 2022


On Wed, 30 Nov 2022 18:43:42 GMT, Andy Goryachev <angorya at openjdk.org> wrote:

>> Fixed memory leak by using weak listeners and making sure to undo everything that has been done to MenuBar/Skin in dispose().
>> 
>> This PR depends on a new internal class ListenerHelper, a replacement for LambdaMultiplePropertyChangeListenerHandler.
>> See https://github.com/openjdk/jfx/pull/908
>
> Andy Goryachev has updated the pull request incrementally with one additional commit since the last revision:
> 
>   8294589: cleanup

modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java line 286:

> 284:         ParentHelper.setTraversalEngine(getSkinnable(), engine);
> 285: 
> 286:         lh.addChangeListener(control.sceneProperty(), true, (scene) -> {

minor: generally, the parenthesis are omitted for lambda's with one parameter (multiple occurences)

modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java line 293:

> 291: 
> 292:             if (scene != null ) {
> 293:                 sceneListenerHelper = new ListenerHelper(MenuBarSkin.this);

Why does this (still) need to rely on a weak reference?  Skins have a well specified life cycle do they not?

modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java line 360:

> 358:                                 break;
> 359:                         default:
> 360:                             break;

minor: indent is incorrect (also in original)

modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java line 374:

> 372: 
> 373:                 // When the parent window looses focus - menu selection should be cleared
> 374:                 sceneListenerHelper.addChangeListener(scene.windowProperty(), true, (sr, oldw, w) -> {

Suggestion:

                sceneListenerHelper.addChangeListener(scene.windowProperty(), true, w -> {

modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java line 381:

> 379: 
> 380:                     if (w != null) {
> 381:                         windowFocusHelper = sceneListenerHelper.addChangeListener(w.focusedProperty(), true, (s, p, focused) -> {

Suggestion:

                        windowFocusHelper = sceneListenerHelper.addChangeListener(w.focusedProperty(), true, focused -> {

modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java line 387:

> 385:                         });
> 386:                     }
> 387:                 });

The goal seems to be to subscribe to changes on scene->window->focused.

This can be done with `flatMap` / `orElse`.

Suggestion:

                // When the parent window looses focus - menu selection should be cleared
                sceneListenerHelper.addChangeListener(
                    scene.windowProperty()
                         .flatMap(Window::focusedProperty)
                         .orElse(false), 
                    true, 
                    focused -> {
                        if (!focused) {
                            unSelectMenus();
                        }
                    }
                );


You could even do this at the top level I think:

    lh.addChangeListener(
        control.sceneProperty()
            .flatMap(Scene::windowProperty)
            .flatMap(Window::focusedProperty)
            .orElse(false), 
        true, 
        focused -> {
            if (!focused) {
                unSelectMenus();
            }
        }
    );

Also available is to make use of `Subscription`:

        Subscription sub = Subscription.subscribe(scene.windowProperty().flatMap(Window::focusedProperty).orElse(false), focused -> {
              if (!focused) {
                  unSelectMenus();
              }
        });

The subscription can then be integrated with `ListenerHelper` again (or even more directly if it accepted `Subscription`):

        lh.addDisconnectable(sub::unsubscribe);

modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java line 436:

> 434:                 if (weakSceneAltKeyEventHandler != null) {
> 435:                     t.removeEventHandler(KeyEvent.ANY, weakSceneAltKeyEventHandler);
> 436:                 }

So, am I correct that `MenuBarSkin` was badly broken before as it never re-registers these weak handlers when the scene changes?  It does re-register the F10 accelerator, but that's all I can see.

So a scenario where I have a MenuBar, and I move it to another Scene, it would basically no longer fully function?

modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java line 1180:

> 1178: 
> 1179:     private static final CssMetaData<MenuBar,Pos> ALIGNMENT =
> 1180:             new CssMetaData<>("-fx-alignment", new EnumConverter<Pos>(Pos.class), Pos.TOP_LEFT ) {

minor: You can use the diamond operator here, probably came from the merge conflict

Suggestion:

            new CssMetaData<>("-fx-alignment", new EnumConverter<>(Pos.class), Pos.TOP_LEFT) {

modules/javafx.controls/src/test/java/test/javafx/scene/control/skin/SkinMemoryLeakTest.java line 227:

> 225:                 ComboBox.class,
> 226:                 DatePicker.class,
> 227:                 //MenuBar.class,

minor: commented out code

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

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


More information about the openjfx-dev mailing list