[prev in list] [next in list] [prev in thread] [next in thread]
List: openjdk-openjfx-dev
Subject: Re: [Rev 01] RFR: 8236840: Memory leak when switching ButtonSkin
From: Jeanette Winzenburg <fastegal () openjdk ! java ! net>
Date: 2020-03-30 10:38:23
Message-ID: Hw6GQAEgH9kqtcQgh4YiRf_kDFcMu8Y03hP03picn54=.9815b589-0cec-4ad8-a4f6-8d8f01e97571 () github ! com
[Download RAW message or body]
On Mon, 30 Mar 2020 07:21:38 GMT, Ambarish Rapte <arapte@openjdk.org> wrote:
> > modules/javafx.controls/src/main/java/javafx/scene/control/skin/ButtonSkin.java \
> > line 178:
> > > 177: @Override public void dispose() {
> > > 178: getSkinnable().sceneProperty().removeListener(weakChangeListener);
> > > 179: super.dispose();
> >
> > +1! Actually, looks like that manual remove is really needed - otherwise we get \
> > an NPE when removing the default button from its parent after the skin has been \
> > switched: @Test
> > public void testDefaultButtonSwitchSkinAndRemove() {
> > Button button = new Button();
> > button.setDefaultButton(true);
> > Group root = new Group(button);
> > Scene scene = new Scene(root);
> > Stage stage = new Stage();
> > stage.setScene(scene);
> > stage.show();
> >
> > button.setSkin(new ButtonSkin1(button));
> > root.getChildren().remove(button);
> > }
> >
> > Note: to see this NPE as failing test (vs. its printout on sysout), we need to \
> > re-wire the uncaughtExceptionHandler, see ComboBoxTest setup/cleanup for an \
> > example.
>
> Thanks for the test case, I did minor changes to it and included in the next \
> commit. The NPE can occur even without `button.setDefaultButton(true);`.
> Please take a look
good catch! You are right, without explicit removal of the listener, the NPE happens \
always.
Actually, I had been sloppy and got distracted by the NPE from my actual goal which \
was to dig into Kevin's "not cleaning up after itself" and .. finally found a \
(concededly extreme corner-case :) where that's happening: when setting the skin to \
null. Two failing tests:
@Test
public void testDefaultButtonNullSkinReleased() {
Button button = new Button();
button.setDefaultButton(true);
Group root = new Group(button);
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
WeakReference<ButtonSkin> defSkinRef = new \
WeakReference<>((ButtonSkin)button.getSkin()); button.setSkin(null);
attemptGC(defSkinRef);
assertNull("skin must be gc'ed", defSkinRef.get());
}
@Test
public void testDefaultButtonNullSkinAcceleratorRemoved() {
Button button = new Button();
button.setDefaultButton(true);
Group root = new Group(button);
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
KeyCodeCombination key = new KeyCodeCombination(KeyCode.ENTER);
assertNotNull(scene.getAccelerators().get(key));
button.setSkin(null);
assertNull(scene.getAccelerators().get(key));
}
An explicitly cleanup in dispose makes them pass:
@Override
public void dispose() {
setDefaultButton(false);
setCancelButton(false);
getSkinnable().sceneProperty().removeListener(weakChangeListener);
-------------
PR: https://git.openjdk.java.net/jfx/pull/147
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic