JDK-8236840 : Memory leak when switching ButtonSkin
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: openjfx11
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2020-01-08
  • Updated: 2020-05-13
  • Resolved: 2020-04-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
openjfx15Fixed
Related Reports
Blocks :  
Relates :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
* OpenJDK 11.0.2
* Redhat 6.8 and Windows 7 64bit. Confident this is agnostic of OS

A DESCRIPTION OF THE PROBLEM :
When the skin of a control is changed the old skin is not garbage collected.

I've traced this to strong reference in an event listener

Specifically com.sun.javafx.scene.control.skin.BehaviorSkinBase adds a listener for context menu request in its constructor. i.e. this line

control.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);

However this listener is never removed in dispose() 

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the test application
Monitor number of skin instances with JVisualVM or other profiler

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Only single instance of skin object, unused instances should be garbage collected
ACTUAL -
Constant increase in number of skin objects (i.e. they're not being garbage collected)

---------- BEGIN SOURCE ----------
import com.sun.javafx.scene.control.skin.ButtonSkin;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SkinMemoryLeak extends Application {

    public static class ButtonSkinCustom1 extends ButtonSkin {
        public ButtonSkinCustom1(Button button) {
            super(button);
        }
    }

    public static class ButtonSkinCustom2 extends ButtonSkin {
        public ButtonSkinCustom2(Button button) {
            super(button);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Button button = new Button("Foo");
        Scene scene = new Scene(button);
        primaryStage.setScene(scene);
        primaryStage.show();
        Timeline animation = new Timeline();
        animation.getKeyFrames().add(new KeyFrame(Duration.seconds(1), event -> {
            if (button.getSkin() instanceof ButtonSkinCustom2) {
                button.setSkin(new ButtonSkinCustom1(button));
            } else {
                button.setSkin(new ButtonSkinCustom2(button));
            }
        }));
        animation.setCycleCount(Animation.INDEFINITE);
        animation.play();
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Can be monkey patched by following in affected skin

@Override
public void dispose() {
    try {
        Field field = BehaviorSkinBase.class.getDeclaredField("contextMenuHandler");
        field.setAccessible(true);
        EventHandler handler = (EventHandler) field.get(this);
        getSkinnable().removeEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, handler);
    } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
        e.printStackTrace();
    }
    super.dispose();
}

FREQUENCY : always



Comments
Changeset: 418675a2 Author: Ambarish Rapte <arapte@openjdk.org> Date: 2020-04-07 01:55:58 +0000 URL: https://git.openjdk.java.net/jfx/commit/418675a2
07-04-2020

The issue with ButtonSkin is a regression of JDK-8209938. However in general if the issue occurs with other skins, then it will be due to a different cause. I don't think we can verify all the skins under this bug. I will try to check few more under this bug.
06-03-2020