-
Bug
-
Resolution: Fixed
-
P4
-
8u74
-
x86
-
windows_7
FULL PRODUCT VERSION :
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) Client VM (build 25.74-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
When selecting a tabulation that DO NOT belong to tabPane.getTabs(), TabPane selection model keep a pointer on this tab and do not release it.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run that attached application with a memory profiler.
Click on "Select Dummy" button -> 1 sample of MyTab Class in the profiler
Click on "Close Dummy" button -> still 1 sample of MyTab Class in the profiler, even after triggering the GC.
The "Dummy tab" will be released only if a new tab is added in TabPane.
When I look in the code of the select method of TabPane, I understand the result :
@Override public void select(Tab tab) {
final int itemCount = getItemCount();
for (int i = 0; i < itemCount; i++) {
final Tab value = getModelItem(i);
if (value != null && value.equals(tab)) {
select(i);
return;
}
}
if (tab != null) {
setSelectedItem(tab);
}
}
If the tab doesn't belng to the TabPane, the setSelectedItem method is called and keep a unnecessary reference on this "tab".
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tabpanebug;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* @author Daniel
*/
public class TabPaneBug extends Application {
private static class MyTab extends Tab {
private int i = 0;
private static int n = 0;
// private Node content;
public MyTab() {
setContent(mkContent());
// selectedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean isSelected) -> {
// if (isSelected) {
// Node content = mkContent();
// setContent(content);
// } else {
// setContent(null);
// }
// });
setClosable(true);
setText("Tab" + n++);
}
private Node mkContent() {
VBox vBox = new VBox();
for (int j = 0; j < 1000; j++) {
HBox hb = new HBox(new Label("bala" + i), new Label("bala" + i), new Label("boo" + i), new Label("boo" + i));
vBox.getChildren().add(hb);
}
i++;
return vBox;
}
}
MyTab dummy = new MyTab();
@Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
// addNTabs(60, tabPane);
Button add = new Button("add");
add.setOnAction((ActionEvent event) -> {
add1Tab(tabPane);
});
Button closeAll = new Button("closeAll");
closeAll.setOnAction((ActionEvent event) -> {
tabPane.getTabs().clear();
});
Button selectDummy = new Button("selectDummy");
selectDummy.setOnAction((ActionEvent event) -> {
tabPane.getSelectionModel().select(dummy);
});
Button closeDummy = new Button("closeDummy");
closeDummy.setOnAction((ActionEvent event) -> {
tabPane.getTabs().remove(dummy);
dummy = null;
});
BorderPane root = new BorderPane();
root.setTop(new ToolBar(selectDummy, closeDummy, add, closeAll));
root.setCenter(tabPane);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Tab Pane memory leak!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void add1Tab(TabPane tabPane) {
MyTab tab = new MyTab();
tabPane.getTabs().add(tab);
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Check if tab belong to tabPane before selecting it
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) Client VM (build 25.74-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
When selecting a tabulation that DO NOT belong to tabPane.getTabs(), TabPane selection model keep a pointer on this tab and do not release it.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run that attached application with a memory profiler.
Click on "Select Dummy" button -> 1 sample of MyTab Class in the profiler
Click on "Close Dummy" button -> still 1 sample of MyTab Class in the profiler, even after triggering the GC.
The "Dummy tab" will be released only if a new tab is added in TabPane.
When I look in the code of the select method of TabPane, I understand the result :
@Override public void select(Tab tab) {
final int itemCount = getItemCount();
for (int i = 0; i < itemCount; i++) {
final Tab value = getModelItem(i);
if (value != null && value.equals(tab)) {
select(i);
return;
}
}
if (tab != null) {
setSelectedItem(tab);
}
}
If the tab doesn't belng to the TabPane, the setSelectedItem method is called and keep a unnecessary reference on this "tab".
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tabpanebug;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* @author Daniel
*/
public class TabPaneBug extends Application {
private static class MyTab extends Tab {
private int i = 0;
private static int n = 0;
// private Node content;
public MyTab() {
setContent(mkContent());
// selectedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean isSelected) -> {
// if (isSelected) {
// Node content = mkContent();
// setContent(content);
// } else {
// setContent(null);
// }
// });
setClosable(true);
setText("Tab" + n++);
}
private Node mkContent() {
VBox vBox = new VBox();
for (int j = 0; j < 1000; j++) {
HBox hb = new HBox(new Label("bala" + i), new Label("bala" + i), new Label("boo" + i), new Label("boo" + i));
vBox.getChildren().add(hb);
}
i++;
return vBox;
}
}
MyTab dummy = new MyTab();
@Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
// addNTabs(60, tabPane);
Button add = new Button("add");
add.setOnAction((ActionEvent event) -> {
add1Tab(tabPane);
});
Button closeAll = new Button("closeAll");
closeAll.setOnAction((ActionEvent event) -> {
tabPane.getTabs().clear();
});
Button selectDummy = new Button("selectDummy");
selectDummy.setOnAction((ActionEvent event) -> {
tabPane.getSelectionModel().select(dummy);
});
Button closeDummy = new Button("closeDummy");
closeDummy.setOnAction((ActionEvent event) -> {
tabPane.getTabs().remove(dummy);
dummy = null;
});
BorderPane root = new BorderPane();
root.setTop(new ToolBar(selectDummy, closeDummy, add, closeAll));
root.setCenter(tabPane);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Tab Pane memory leak!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void add1Tab(TabPane tabPane) {
MyTab tab = new MyTab();
tabPane.getTabs().add(tab);
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Check if tab belong to tabPane before selecting it