<div dir="ltr"><div class="gmail_default" style="font-family:verdana,sans-serif">Hi John,</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">Thanks for the prompt response. Yes, I looked into the code afterwards, and I can see that it is just fairly oblivious to the waste. I was looking to override with nulls myself, but there were too many privates.</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">The only solution (without re-writing my own impl) is to re-create the SortedList every time the dataset is smaller, or to create some sort of DTO that does not link to the original real objects in any way (or maybe use WeakReferences). In my case, I could have 10,000 rows where each item's underlying memory is around 500KB on the heap. That's instantly 5GB I cannot re-claim, or example.</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">That code with the "bug" <i>mostly</i> hasn't changed in 10+ years. Can it be fixed? It's such a fundamental class, adding workarounds (everywhere, forever) would seem a shame.</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">Kind Regards,</div><div class="gmail_default" style="font-family:verdana,sans-serif">Cormac</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 4 Dec 2025 at 20:01, John Hendrikx <<a href="mailto:john.hendrikx@gmail.com" target="_blank">john.hendrikx@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>

  
    
  
  <div>
    <p>This looks like a classic problem where unused elements in an
      array are not nulled.  Looking at the code that updates the `size`
      field, there are a few code paths that are not setting unused
      elements to null.  I also saw no code that ever shrinks the
      arrays.  So it looks this implementation is only half finished.<br>
    </p>
    <p>So, yes, I'd say this is a bug.<br>
    </p>
    <p>
      </p><blockquote type="cite">I'm going to guess there's a very good
        reason for this behaviour.</blockquote>
      Time pressure and insufficient testing.<br>
    <p></p>
    <p>--John<br>
    </p>
    <div>On 04/12/2025 19:40, Cormac Redmond
      wrote:<br>
    </div>
    <blockquote type="cite">
      
      <div dir="ltr">
        <div class="gmail_default" style="font-family:verdana,sans-serif">Hi,<br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif">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.</div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif">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.</div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif">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.</div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif">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.</div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><img src="cid:ii_19aeafa7bfdf32177cc1" alt="sorted_list_mem.gif" width="406" height="562"><br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><br>
        </div>
        <div class="gmail_default" style="font-family:verdana,sans-serif">Code to reproduce:</div>
        <div class="gmail_default" style="font-family:verdana,sans-serif"><br>
        </div>
        <div class="gmail_default"><font face="monospace">public
            class SortedListMemWasteDemo extends Application {<br>
            <br>
                record PotentialLargeData(String info) {}<br>
            <br>
                public static void main(String[] args) {<br>
                    launch(args);<br>
                }<br>
            <br>
                @Override<br>
                public void start(Stage primaryStage) {<br>
                    ObservableList<PotentialLargeData> masterData
            = FXCollections.observableArrayList(<br>
                            new PotentialLargeData("Initial info 1"), <br>
                            new PotentialLargeData("Initial info 2"));<br>
            <br>
                    TableColumn<PotentialLargeData, String> col =
            new TableColumn<>("Info");<br>
                    col.setCellValueFactory(cellData -> new
            SimpleStringProperty(cellData.getValue().info()));<br>
                    <br>
                    TableView<PotentialLargeData> table = new
            TableView<>();<br>
                    table.getColumns().add(col);<br>
            <br>
                    FilteredList<PotentialLargeData> filteredData
            = new FilteredList<>(masterData, item -> true);<br>
                    SortedList<PotentialLargeData> sortedData =
            new SortedList<>(filteredData);<br>
                   
            sortedData.comparatorProperty().bind(table.comparatorProperty());<br>
                    table.setItems(sortedData);<br>
            <br>
                    Button loadManyBtn = new Button("Add 10,000 items");<br>
                    loadManyBtn.setOnAction(e -> {<br>
                        System.out.println("Adding 10000 items to master
            data");<br>
                        masterData.clear();<br>
                        for (int i = 0; i < 10000; i++)
            masterData.add(new PotentialLargeData("Info item " + i));<br>
                    });<br>
            <br>
                    Button reduceBtn = new Button("Reduce to 2");<br>
                    reduceBtn.setOnAction(e -> {<br>
                        System.out.println("Reducing master data size to
            2");<br>
                        masterData.setAll(new PotentialLargeData("New
            info 1"), new PotentialLargeData("New info 2"));<br>
                    });<br>
            <br>
                    Button printBtn = new Button("Print interal sizes");<br>
                    printBtn.setOnAction(e -> {<br>
                        try {<br>
                            System.out.println("Items the user sees: " +
            sortedData.size());<br>
                            int[] filtered = (int[])
            getFieldValue(filteredData, "filtered");<br>
                            System.out.println("FilteredList.filtered
            length: " + filtered.length);<br>
                            // This is a hidden and significant waste of
            memory<br>
                            Object[] sorted = (Object[])
            getFieldValue(sortedData, "sorted");<br>
                            System.out.println("SortedList.sorted
            length: " + sorted.length);<br>
                        } catch (Exception ex) {<br>
                            throw new RuntimeException(ex);<br>
                        }<br>
                    });<br>
            <br>
                    primaryStage.setScene(new Scene(new VBox(table,
            loadManyBtn, reduceBtn, printBtn), 600, 400));<br>
                    primaryStage.show();<br>
                }<br>
            <br>
                private static Object getFieldValue(Object object,
            String fieldName) throws Exception {<br>
                    Field field =
            object.getClass().getDeclaredField(fieldName);<br>
                    field.setAccessible(true);<br>
                    return field.get(object);<br>
                }<br>
            }</font></div>
        <div class="gmail_default"><font face="monospace"><br>
          </font></div>
        <div class="gmail_default"><font face="monospace"><br>
          </font></div>
        <div class="gmail_default"><font face="verdana, sans-serif">Kind Regards,</font></div>
        <div class="gmail_default"><font face="verdana, sans-serif">Cormac</font></div>
      </div>
    </blockquote>
  </div>
</blockquote></div>