JDK-8088394 : Huge memory consumption in TableView with too many columns
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: 8
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2013-06-28
  • Updated: 2018-09-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
I tried to look at related open issues before inserting this one, but I couldn't find any that stresses this problem. So sorry if this is a duplicate.

The issue is that whenever I have too many columns, the memory consumption of TableView is huge.

I noticed that the number of columns is much more problematic that the number of rows.
For example, a table with 100,000 rows and few columns has very reasonable memory consumtion, and scrolls very smoothly. Whereas with just 10 rows and 500 columns, memory quickly exceeds 400MB, and horizontal scrolling becomes very slow.

It seems that only rows take advantage of the cell reuse policy, while columns don't?
To fix this we need to virtualise and reuse table cells (as well as table column header cells). This work needs to be done in TableRowSkinBase::updateCells and NestedTableColumnHeader::updateTableColumnHeaders, however this work is too risky for an update release and should be done when there is more bake time, so I'm retargeting to 9.

Requesting deferral as it is too late in the release to be considering risky refactorings.

Attaching a very early proof of concept patch. Unfortunately it is buggy and incomplete. Refer to RT-31804 for more context on the issue.

Currently, when using fixed cell size mode, even though only a subset of the 500 columns worth of cells are visible, they are all created up front (although they are not all added to the scenegraph - only the visible columns cells are added to the scenegraph). A better approach would be to only create enough cells in each row for the visible area and to reuse these cells as the horizontal scrollbar is moved. This will result in significantly less memory usage as there would be 24 cells per row (approximately), rather than 500, that need to sit in memory.

Leaving open as horizontal scrolling is very slow.

No, there is no known way around this issue in 2.2.21.

Actually, I am using the 2.2.21 release. So is this memory consumption somehow avoidable for a large number of columns, in 2.2.21?

Columns are not virtualised like rows, unless you're using JavaFX 8.0 and call tableView..setFixedCellSize(24);

Here's an SSCCE. 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.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.cell.PropertyValueFactory; import javafx.stage.Stage; public class BigTableViewSample extends Application { public BigTableViewSample() { } public static void main(String[] args) { Application.launch(args); } @SuppressWarnings("unchecked") @Override public void start(Stage stage) { ObservableList<Item> items = FXCollections.observableArrayList(); for (int row=0; row<10; row++) { items.add(new Item("item" + row)); } TableView<Item> tableView = new TableView<>(items); tableView.setFixedCellSize(24); for (int column=0; column<500; column++) { TableColumn col = new TableColumn("Col " + column); col.setCellValueFactory(new PropertyValueFactory<>("name")); tableView.getColumns().add(col); } Scene scene = new Scene(tableView, 1100, 900); stage.setScene(scene); stage.setTitle("Big TableView Sample"); stage.show(); } public static class Item { private StringProperty name; public Item(String name) { setName(name); } public void setName(String value) { nameProperty().set(value); } public String getName() { return nameProperty().get(); } public StringProperty nameProperty() { if (name == null) { name = new SimpleStringProperty(this, "name"); } return name; } public String toString() { return getName(); } } }