package testjavafx; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.stage.Stage; /** * * @author scott */ public class FxMain extends Application { /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Java FX UI Tester - Mneumonic leak"); HBox root = new HBox(); ButtonMneumonicTest.replaceButton(root); root.getChildren().add(new Label(" <-- Press the button multiple times. The button is replaced each time, but JavaFX is holding a reference to the old ones.")); primaryStage.setScene(new Scene(root)); primaryStage.show(); } } class ButtonMneumonicTest { static boolean leakyVersion = true; // change to see how Mneumonics affect object retention static int number; HBox parent; byte [] data = new byte [0x1000000]; Button b; private ButtonMneumonicTest(HBox parent) { this.parent = parent; number++; b = new Button(); // this is the workaround //b.setMnemonicParsing(false); if (leakyVersion) { // Using this text it leaks b.setText("Has_Underscore "+number); } else { // But with this text it doesn't leak b.setText("No Underscore "+number); } b.setUserData(this); b.setOnAction(new EventHandler() { public void handle(ActionEvent t) { Button oldButton = (Button) t.getSource(); ButtonMneumonicTest oldBigObj = (ButtonMneumonicTest) oldButton.getUserData(); System.err.println("Done with \"" + oldButton.getText() + "\". Replacing with a new one."); // pressing the button makes a new large object that is only reachable via the button itself HBox oldParent = oldBigObj.parent; replaceButton(oldParent); } }); } static void replaceButton(HBox parent) { ButtonMneumonicTest bigObj = new ButtonMneumonicTest(parent); parent.getChildren().setAll(bigObj.b); } }