我在 VirtualFlow 中遇到了一种行为,不确定这是否是内存泄漏或者是否是这样设计的。
在 TableView 中,我希望当不使用 TableRow 实例时,它们应该能够被垃圾收集。但是,当我按照以下步骤操作时:
Step #1:我打开了一个带有 TableView 的阶段,其中没有数据。我检查了内存中是否有 TableRow 的实时实例,但没有看到任何实例(这是我所期望的)。
Step #2:我用一些数据填充了 TableView,这会根据所需的空间生成 TableRows。在我的场景中,假设它生成了 38 个 TableRow 对象的实例。
Step #3:我清除了 TableView 中的项目并显示占位符消息。当我检查 TableRow 实例时(执行多次 GC 后),它仍然具有 VirtualFlow 中强烈保留的 TableRows 的所有 38 个引用cells
ArrayLinkedlist,如下图所示:
这是内存泄漏还是已经考虑到的预期行为?
这是我用来检查上述场景的代码:
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TableViewMemoryLeakDemo extends Application {
public static void main(String... a) {
Application.launch(a);
}
@Override
public void start(final Stage primaryStage) {
final ObservableList<TableDataObj> items = FXCollections.observableArrayList();
final TableView<TableDataObj> table = buildTable();
table.setItems(items);
Button clear = new Button("Clear All");
clear.setOnAction(e -> {
items.clear();
});
Button add = new Button("Add 50 Rows");
add.setOnAction(e -> {
for (int i = 0 + 1; i < 50; i++) {
final String firstName = "First Name " + i;
final String lastName = "Last Name " + i;
final String city = "City " + i;
items.add(new TableDataObj(i, firstName, lastName, city));
}
});
HBox row = new HBox(add, clear);
row.setSpacing(10);
final VBox root = new VBox(row, table);
root.setSpacing(10);
root.setPadding(new Insets(10));
VBox.setVgrow(table, Priority.ALWAYS);
final Scene sc = new Scene(root, 1200, 950);
primaryStage.setScene(sc);
primaryStage.setTitle("TableView MemoryLeak Demo " + System.getProperty("javafx.runtime.version"));
primaryStage.show();
}
private TableView<TableDataObj> buildTable() {
final TableColumn<TableDataObj, Integer> idCol = new TableColumn<>();
idCol.setText("Id");
idCol.setCellValueFactory(param -> param.getValue().idProperty().asObject());
idCol.setPrefWidth(100);
final TableColumn<TableDataObj, String> fnCol = new TableColumn<>();
fnCol.setText("First Name");
fnCol.setCellValueFactory(param -> param.getValue().firstNameProperty());
fnCol.setPrefWidth(150);
final TableColumn<TableDataObj, String> lnCol = new TableColumn<>();
lnCol.setText("Last Name");
lnCol.setCellValueFactory(param -> param.getValue().lastNameProperty());
lnCol.setPrefWidth(150);
final TableColumn<TableDataObj, String> cityCol = new TableColumn<>();
cityCol.setText("City");
cityCol.setCellValueFactory(param -> param.getValue().cityProperty());
cityCol.setPrefWidth(150);
final TableView<TableDataObj> tableView = new TableView<>();
tableView.getColumns().addAll(idCol, fnCol, lnCol, cityCol);
return tableView;
}
/**
* Data object.
*/
class TableDataObj {
private final IntegerProperty id = new SimpleIntegerProperty();
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final StringProperty city = new SimpleStringProperty();
public TableDataObj(final int i, final String fn, final String ln, final String cty) {
setId(i);
setFirstName(fn);
setLastName(ln);
setCity(cty);
}
public StringProperty cityProperty() {
return city;
}
public StringProperty firstNameProperty() {
return firstName;
}
public int getId() {
return id.get();
}
public IntegerProperty idProperty() {
return id;
}
public StringProperty lastNameProperty() {
return lastName;
}
public void setCity(final String city1) {
city.set(city1);
}
public void setFirstName(final String firstName1) {
firstName.set(firstName1);
}
public void setId(final int idA) {
id.set(idA);
}
public void setLastName(final String lastName1) {
lastName.set(lastName1);
}
}
}