Details
-
Bug
-
Resolution: Fixed
-
P3
-
8u5, 8u20
-
win7x32
Description
Suppose we have N columns in TableView, let's remove R columns. I expect to see (N-R) TableColumn instances in profiler after forced garbage collection, but there are still N of them. lets add M columns and we'll have (N+M) instances instead of (N-R+M), so columns are not even reused.
The only way i've found to free all instances it's to gc whole TableView.
Steps to reproduce:
1) add few columns
2) check TableColumn instance's count in profiler
3) remove few columns
4) collect garbage to make sure
5) check profiler again, none of TableColumns were garbage collected
Tested on 8u5 and 8u20b22.
Here is my code for testing:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class NewFXMain extends Application {
private int rows = 0;
private int cols = 0;
public class TestDataClass {
private final StringProperty testValue;
public TestDataClass() {
testValue = new SimpleStringProperty("value");
}
public TestDataClass(String s) {
testValue = new SimpleStringProperty(s);
}
public String getTestValue() {
return testValue.get();
}
public void setTestValue(String value) {
testValue.set(value);
}
public StringProperty testValueProperty() {
return testValue;
}
}
@Override
public void start(Stage primaryStage) {
ObservableList<TestDataClass> list = FXCollections.observableArrayList();
for (int i = 0; i < 4; i++) {
list.add(new TestDataClass("val " + rows++));
}
TableView table = new TableView(list);
Button rAdd = new Button("add");
rAdd.setOnAction((evt) -> {
list.add(new TestDataClass("val " + rows++));
});
Button rDel = new Button("remove");
rDel.setOnAction((evt) -> {
if (!list.isEmpty()) {
rows--;
int size = list.size();
list.remove(size - 1);
}
});
Button cAdd = new Button("add");
cAdd.setOnAction((evt) -> {
TableColumn<TestDataClass, String> col = new TableColumn<>();
col.setCellValueFactory(new PropertyValueFactory<>("testValue"));
col.setText("col " + cols++);
table.getColumns().add(col);
});
Button cDel = new Button("remove");
cDel.setOnAction((evt) -> {
ObservableList columns = table.getColumns();
if (!columns.isEmpty()) {
cols--;
int size = columns.size();
columns.remove(size - 1);
}
});
GridPane grid = new GridPane();
grid.setVgap(3);
grid.setHgap(3);
grid.addRow(0, new Label("row:"), rAdd, rDel);
grid.addRow(1, new Label("col:"), cAdd, cDel);
Button clr = new Button("Remove All");
VBox root = new VBox(table, grid, clr);
root.setSpacing(3);
clr.setOnAction((evt) -> {
root.getChildren().clear();
list.clear();
});
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The only way i've found to free all instances it's to gc whole TableView.
Steps to reproduce:
1) add few columns
2) check TableColumn instance's count in profiler
3) remove few columns
4) collect garbage to make sure
5) check profiler again, none of TableColumns were garbage collected
Tested on 8u5 and 8u20b22.
Here is my code for testing:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class NewFXMain extends Application {
private int rows = 0;
private int cols = 0;
public class TestDataClass {
private final StringProperty testValue;
public TestDataClass() {
testValue = new SimpleStringProperty("value");
}
public TestDataClass(String s) {
testValue = new SimpleStringProperty(s);
}
public String getTestValue() {
return testValue.get();
}
public void setTestValue(String value) {
testValue.set(value);
}
public StringProperty testValueProperty() {
return testValue;
}
}
@Override
public void start(Stage primaryStage) {
ObservableList<TestDataClass> list = FXCollections.observableArrayList();
for (int i = 0; i < 4; i++) {
list.add(new TestDataClass("val " + rows++));
}
TableView table = new TableView(list);
Button rAdd = new Button("add");
rAdd.setOnAction((evt) -> {
list.add(new TestDataClass("val " + rows++));
});
Button rDel = new Button("remove");
rDel.setOnAction((evt) -> {
if (!list.isEmpty()) {
rows--;
int size = list.size();
list.remove(size - 1);
}
});
Button cAdd = new Button("add");
cAdd.setOnAction((evt) -> {
TableColumn<TestDataClass, String> col = new TableColumn<>();
col.setCellValueFactory(new PropertyValueFactory<>("testValue"));
col.setText("col " + cols++);
table.getColumns().add(col);
});
Button cDel = new Button("remove");
cDel.setOnAction((evt) -> {
ObservableList columns = table.getColumns();
if (!columns.isEmpty()) {
cols--;
int size = columns.size();
columns.remove(size - 1);
}
});
GridPane grid = new GridPane();
grid.setVgap(3);
grid.setHgap(3);
grid.addRow(0, new Label("row:"), rAdd, rDel);
grid.addRow(1, new Label("col:"), cAdd, cDel);
Button clr = new Button("Remove All");
VBox root = new VBox(table, grid, clr);
root.setSpacing(3);
clr.setOnAction((evt) -> {
root.getChildren().clear();
list.clear();
});
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}