JDK-8264330 : Scene MouseHandler is referencing removed nodes
  • Type: Bug
  • Component: javafx
  • Sub-Component: scenegraph
  • Affected Version: jfx16
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2021-03-28
  • Updated: 2021-12-23
  • Resolved: 2021-04-01
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
jfx17Fixed
Related Reports
Relates :  
Relates :  
Description
The Scene$MouseHandler is holding on to a reference of nodes that have been removed from the Scene. This can cause issues when navigating through an application with the keyboard only (Nodes do not get garbage collected until a mouse click occurs).

Run the below example, and follow it carefully:

1) Click the *label* with the left mouse button
2) Select the button with the keyboard (press space)

Notice the the Button is removed from the Scene but is not being garbage collected.  This is because Scene$MouseHandler pdrEventTargets field still contains a reference to the button, which is not being cleaned up because pdrInProgress is false when the removal code (which is supposed to do the clean up) runs.

Sample code below:

    import java.lang.ref.WeakReference;
    
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class FrontEndRunner extends Application {
    
      public static void main(String[] args) {
        Application.launch(args);
      }
    
      @Override
      public void start(Stage stage) throws Exception {
        Button button = new Button("(2) Select me with keyboard");
        VBox box = new VBox(new HBox(button, new Label("(1) Click here first!")));
        WeakReference<Button> weakRef = new WeakReference<>(button);
    
        button.setOnAction(e -> {
          Label label = new Label("Thanks, button reference should disappear");
          box.getChildren().setAll(label);
    
          new Thread(() -> {
            for(;;) {
              try {
                Thread.sleep(1000);
                System.gc();
    
                Platform.runLater(() -> {
                  label.setText("Button reference is: " + weakRef.get());
                });
              }
              catch(InterruptedException e1) {
                e1.printStackTrace();
              }
            }
          }).start();
        });
    
        button = null;
    
        Scene scene = new Scene(box);
    
        stage.setWidth(1000);
        stage.setScene(scene);
        stage.show();
      }
    }
Comments
Changeset: 015dad07 Author: John Hendrikx <jhendrikx@openjdk.org> Committer: Kevin Rushforth <kcr@openjdk.org> Date: 2021-04-01 12:49:39 +0000 URL: https://git.openjdk.java.net/jfx/commit/015dad07
01-04-2021

Submitted solution + test in https://github.com/openjdk/jfx/pull/448
31-03-2021

Think I also have a fix. There is code in place that clears the press-drag-release values (clearPDRTargets) when MOUSE_RELEASED event comes in, but this code doesn't clear the pdrEventTargets list. When the list is filled again (due to a MOUSE_PRESSED, done in fillHierarchy) there is an explicit list.clear() done there which cleans up the old references, but this should happen much earlier. Suggestion therefore is to add one line to clearPDRTargets which also clears the pdrEventTargets list.
28-03-2021