-
Bug
-
Resolution: Unresolved
-
P3
-
jfx17
ADDITIONAL SYSTEM INFORMATION :
Windows, Linux, JDK 17
A DESCRIPTION OF THE PROBLEM :
Memory is not released on tab removal, when the tab has a ContextMenu with MenuItem, that has reference to the tab.
Works well on javafx version 17-ea+12, but since 17-ea+13 problem exists.
I think that the problem is in ControlAcceleratorSupport.java. Since this version a listener is always added to the "anchor", which is a TabPane in this case, so removing the tab does not release memory.
REGRESSION : Last worked in version 16
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Result on 17-ea+12. Memory used after the tab is removed should be similar to memory used without the tab.
without tab - used mem: 20 MB
with tab, after gc - used mem: 76 MB
Tab removed
removed tab, after gc - used mem: 26 MB
ACTUAL -
Result on version 18 (same on 17-ea+13):
without tab - used mem: 16 MB
with tab, after gc - used mem: 76 MB
Tab removed
removed tab, after gc - used mem: 74 MB
---------- BEGIN SOURCE ----------
public class TabsPaneControllerTest extends Application {
private static final int LARGE_MEM_BYTES = 50_000_000;
public static void main(String[] s) {
launch();
}
@Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
Scene scene = new Scene(tabPane);
primaryStage.setScene(scene);
primaryStage.setMinHeight(300);
primaryStage.setMinWidth(300);
primaryStage.show();
new Thread(() -> {
runGc();
printMem("without tab");
addTab(tabPane);
runGc();
printMem("with tab, after gc");
Platform.runLater(() -> {
tabPane.getTabs().remove(0);
System.out.println("Tab removed");
});
// wait for tab removal
while (!tabPane.getTabs().isEmpty()) {
sleep(500);
}
runGc();
printMem("removed tab, after gc");
Platform.exit();
})
.start();
}
public void addTab(TabPane tabPane) {
Platform.runLater(() -> {
Tab tab = new Tab("HeavyTab");
tab.setUserData(new byte[LARGE_MEM_BYTES]);
tab.setContextMenu(new ContextMenu());
// reference to tab in MenuItem (onAction)
MenuItem menuItemWithReferenceToTab = new MenuItem("RenameTabMenuItem");
menuItemWithReferenceToTab.setOnAction(e -> tab.setText("tab renamed"));
tab.getContextMenu().getItems().add(menuItemWithReferenceToTab);
tabPane.getTabs().add(tab);
});
}
public static void runGc() {
System.gc();
sleep(2000);
}
private static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (Exception e) {
e.printStackTrace(); // ignore
}
}
public static void printMem(String message) {
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory() / 1024 / 1024;
long freeMemory = runtime.freeMemory() / 1024 / 1024;
System.out.println(message + "\t - used mem: " + (totalMemory - freeMemory) + " MB");
}
}
---------- END SOURCE ----------
FREQUENCY : always
Windows, Linux, JDK 17
A DESCRIPTION OF THE PROBLEM :
Memory is not released on tab removal, when the tab has a ContextMenu with MenuItem, that has reference to the tab.
Works well on javafx version 17-ea+12, but since 17-ea+13 problem exists.
I think that the problem is in ControlAcceleratorSupport.java. Since this version a listener is always added to the "anchor", which is a TabPane in this case, so removing the tab does not release memory.
REGRESSION : Last worked in version 16
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Result on 17-ea+12. Memory used after the tab is removed should be similar to memory used without the tab.
without tab - used mem: 20 MB
with tab, after gc - used mem: 76 MB
Tab removed
removed tab, after gc - used mem: 26 MB
ACTUAL -
Result on version 18 (same on 17-ea+13):
without tab - used mem: 16 MB
with tab, after gc - used mem: 76 MB
Tab removed
removed tab, after gc - used mem: 74 MB
---------- BEGIN SOURCE ----------
public class TabsPaneControllerTest extends Application {
private static final int LARGE_MEM_BYTES = 50_000_000;
public static void main(String[] s) {
launch();
}
@Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
Scene scene = new Scene(tabPane);
primaryStage.setScene(scene);
primaryStage.setMinHeight(300);
primaryStage.setMinWidth(300);
primaryStage.show();
new Thread(() -> {
runGc();
printMem("without tab");
addTab(tabPane);
runGc();
printMem("with tab, after gc");
Platform.runLater(() -> {
tabPane.getTabs().remove(0);
System.out.println("Tab removed");
});
// wait for tab removal
while (!tabPane.getTabs().isEmpty()) {
sleep(500);
}
runGc();
printMem("removed tab, after gc");
Platform.exit();
})
.start();
}
public void addTab(TabPane tabPane) {
Platform.runLater(() -> {
Tab tab = new Tab("HeavyTab");
tab.setUserData(new byte[LARGE_MEM_BYTES]);
tab.setContextMenu(new ContextMenu());
// reference to tab in MenuItem (onAction)
MenuItem menuItemWithReferenceToTab = new MenuItem("RenameTabMenuItem");
menuItemWithReferenceToTab.setOnAction(e -> tab.setText("tab renamed"));
tab.getContextMenu().getItems().add(menuItemWithReferenceToTab);
tabPane.getTabs().add(tab);
});
}
public static void runGc() {
System.gc();
sleep(2000);
}
private static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (Exception e) {
e.printStackTrace(); // ignore
}
}
public static void printMem(String message) {
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory() / 1024 / 1024;
long freeMemory = runtime.freeMemory() / 1024 / 1024;
System.out.println(message + "\t - used mem: " + (totalMemory - freeMemory) + " MB");
}
}
---------- END SOURCE ----------
FREQUENCY : always
- relates to
-
JDK-8268374 MenuItem's accelerator gets fired even when ContextMenu is set to null
- Open
-
JDK-8274022 Additional Memory Leak in ControlAcceleratorSupport
- Resolved
-
JDK-8244075 Accelerator of ContextMenu's MenuItem is not removed when ContextMenu is removed from Scene
- Resolved