SortedList hanging on to references & preventing GC
Cormac Redmond
credmond at certak.com
Thu Dec 4 18:40:41 UTC 2025
Hi,
I've traced a memory issue back to a SortedList (surprisingly), where it's
hanging on to objects that could/should have been GC'd.
SortedList's internal arrays will only grow (but not shrink) in line with
the source ObservableList (I'm sure there are reasons for this). So even
when your source ObservableList shrinks, SortedList is hanging on to
references to objects the source list once contained, even though they're
completely removed from the source list.
This leads to a substantial waste of memory, especially when just one
momentarily large dataset leads to a permanent spike in unnecessary memory
usage for the remainder for the application's lifetime. I'm sure I'm not
the first to raise this or ask about it & I'm going to guess there's a very
good reason for this behaviour. But could someone explain this? I would
have thought SortedList should/could remain as lean as the source list.
FilteredList is somewhat similar, except it stores an array of ints
(indexes), so less of a memory hit, but still pointless nonetheless.
Example GIF + code below: setup a typical sortable table (so,
ObservableList + FilteredList + SortableList), where the print button shows
that when you add a lot of data, and remove it, SortedList retains
references to all of the old objects. Some reflection is used to print that
information.
[image: sorted_list_mem.gif]
Code to reproduce:
public class SortedListMemWasteDemo extends Application {
record PotentialLargeData(String info) {}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
ObservableList<PotentialLargeData> masterData =
FXCollections.observableArrayList(
new PotentialLargeData("Initial info 1"),
new PotentialLargeData("Initial info 2"));
TableColumn<PotentialLargeData, String> col = new
TableColumn<>("Info");
col.setCellValueFactory(cellData -> new
SimpleStringProperty(cellData.getValue().info()));
TableView<PotentialLargeData> table = new TableView<>();
table.getColumns().add(col);
FilteredList<PotentialLargeData> filteredData = new
FilteredList<>(masterData, item -> true);
SortedList<PotentialLargeData> sortedData = new
SortedList<>(filteredData);
sortedData.comparatorProperty().bind(table.comparatorProperty());
table.setItems(sortedData);
Button loadManyBtn = new Button("Add 10,000 items");
loadManyBtn.setOnAction(e -> {
System.out.println("Adding 10000 items to master data");
masterData.clear();
for (int i = 0; i < 10000; i++) masterData.add(new
PotentialLargeData("Info item " + i));
});
Button reduceBtn = new Button("Reduce to 2");
reduceBtn.setOnAction(e -> {
System.out.println("Reducing master data size to 2");
masterData.setAll(new PotentialLargeData("New info 1"), new
PotentialLargeData("New info 2"));
});
Button printBtn = new Button("Print interal sizes");
printBtn.setOnAction(e -> {
try {
System.out.println("Items the user sees: " +
sortedData.size());
int[] filtered = (int[]) getFieldValue(filteredData,
"filtered");
System.out.println("FilteredList.filtered length: " +
filtered.length);
// This is a hidden and significant waste of memory
Object[] sorted = (Object[]) getFieldValue(sortedData,
"sorted");
System.out.println("SortedList.sorted length: " +
sorted.length);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
});
primaryStage.setScene(new Scene(new VBox(table, loadManyBtn,
reduceBtn, printBtn), 600, 400));
primaryStage.show();
}
private static Object getFieldValue(Object object, String fieldName)
throws Exception {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(object);
}
}
Kind Regards,
Cormac
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20251204/cfaa2788/attachment-0001.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sorted_list_mem.gif
Type: image/gif
Size: 128014 bytes
Desc: not available
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20251204/cfaa2788/sorted_list_mem-0001.gif>
More information about the openjfx-dev
mailing list