On 8/14/2026 5:45 PM, Dan Smith wrote:
On Aug 14, 2026, at 12:58 PM, Kevin Rushforth <[email protected]>
wrote:
On 8/6/2026 2:52 PM, Kevin Rushforth wrote:
On 8/6/2026 1:33 PM, Dan Smith wrote:
On Aug 3, 2026, at 8:34 AM, Kevin Rushforth <[email protected]> wrote:
First, some JavaFX UI controls APIs take a list of objects. JavaFX internally
creates a WeakReference to an individual object in some cases. This is tracked
by JDK-8387301 [2].
I was curious about the use cases, and how they might react to different
reference-clearing policies, so I dug into the code.
For TablePosition (#2 in JDK-8387301), turns out the WeakReference seems to go
unused: I can delete the private `itemRef` field and the package-access
`getItem` method, and everything still builds fine*.
(*Caveat that this is a complicated system, I’ve only built on macOS and
haven’t run any tests. Any reason to believe this is a platform-specific
dependency?)
I’ll keep looking at the other cases, see if they can provide any insight into
reference-clearing policies...
Thanks for looking into this. I know we don't rely on any reference clearing
behavior, but I haven't dug into how we are using those WeakReferences in UI controls
or whether we still need them at all (unlike the WebView Java <--> JS bridge
case where I know how and why we are using them). It would be interesting to dig
deeper to see if they are needed at all for controls.
The private itemRef in TablePosition that you pointed out is indeed unused and
can be removed. The other three are still needed.
We don't rely on reference clearing behavior, so a simple wrapper will suffice
for our needs.
Although... we do care about not introducing memory leaks.
I’ve been playing around with SelectedItemsReadOnlyObservableList, again with
the goal of finding a use case that could inform our design choices.
Here, I’m stuck trying to find a scenario where the WeakReference could get
cleared:
The idea is that there’s a list of indices of selected items in an
ObservableList, and another list that acts as a view of the subset of
ObservableList items that are selected. The subset view keeps around a cache of
last-seen selected items so that it can create a “removed” change event that
shares the no-longer-selected item with listeners.
There’s some worry that the cache of last-seen selected items will cause a
memory leak, so they are wrapped in WeakReferences. But… if they’re still in
the underlying list, they won’t be GC’ed. And when I remove them from the list,
the selection always seems to be auto-updated to a new, non-GC'ed selection,
without creating any change events referencing the removed object.
If there *were* a way to trigger a change event referring to an object that had
been GC’ed, then the removal change event would reference ’null’, which would
be a different problem.
Agreed. This seems odd and worth a follow-up. The current design doesn't
seem robust. If the weak reference were ever reclaimed before the change
removal notification was delivered, reporting "null" as a removed item
would likely cause problems (e.g., confuse applications that are not
expecting a null item).
The case of oldRowItemRef in TableCell and TreeTableCell are a more
typical "cache the last thing seen" and are used as an optimization, but
even those should be reexamined.
(I experimented with some exotic ObservableLists that have unstable
results—e.g., each read allocates a new object—and then all sorts of weird
things happen, but this seems outside the intended scope of the API.)
As this relates to value objects:
- If memory leaks are a problem for identity objects, wouldn’t they also be a
problem for value objects (which might, e.g., reference a large array)? Which
would call for immediately discarding the value objects—but if that works, why
have a cache at all?
- If nulls in removal change events are the problem we need to avoid for value
objects, then that calls for never releasing the value objects. But wouldn’t
that also be a problem for identity objects? In that case, WeakReferences seem
like the wrong solution, and the cache should just hold direct references to
everything.
That's an interesting question. And one I'll need to drill into when I
get to the WebView Java <--> JS bridge implementation. Up until now,
I've been assuming that we don't care much about whether value objects
leak. For small objects like the boxed primitive classes, that seems a
reasonable assumption (what does it mean to "leak" an integer?).
However, if a user-defined value object (e.g., a simple value record)
contains a large array of integers or a reference to a user-defined
identity object, then I think we do care. In that case, the solution
that I'm proposing could lead to memory leaks in the case of
user-defined value objects.
For example, if the application creates the following record:
value record Container(Integer[] indices);
Container container = new Container(indices);
jsObject.setMember("myContainer")
The updated native JS bridge code will create a JGlobalRef (instead of a
JWeakGlobalRef), since the parameter is a value object, which will leak.
Do I understand this correctly? If so, then we might need to rethink this.
-- Kevin