[External] : Re: Virtual thread memory leak because TrackingRootContainer keeps threads

Alan Bateman Alan.Bateman at oracle.com
Thu Jul 11 09:00:41 UTC 2024


On 10/07/2024 20:56, Michal Domagala wrote:
> Hi,
>
> My issue is accepted as Bug ID: JDK-8336061 Virtual threads cannot be 
> GC'ed before they terminate (java.com) 
> <https://bugs.java.com/bugdatabase/view_bug?bug_id=JDK-8336061> and 
> solved as documentation defect.

You can view the issue in JBS here: 
https://bugs.openjdk.org/browse/JDK-8336061

As noted, we missed an update to a paragraph when copying text from JEP 
425/436 to JEP 444 so it wasn't aligned with the change summarized in 
the History section of JEP 444. That has been fixed. Otherwise, we can 
only close the JBS issue for now as support for some kind of ephemeral 
threads is definitely future exploration.

>
> Still one issue is not clear to me. Is VT strongly referenced because 
> being blocked and GC'ed is not useful or is problematic to implement?
>
> There were voices in discussion that VT GC is surprising for 
> developers. For that reason I showed my case. I wanted to present that 
> "closing" blocking queue by nullifying and GC does not need 
> synchronization. Unsynchronized code is less prone to errors than code 
> with synchronization.

In some cases then having the producer offering a special element to 
mean "done" can work as a signal to the consumer to shutdown. Some form 
of closable channel would be similar. I think this project has a good 
handle on possible use-cases so we know well that it's not as simple as 
this in some cases.

In the Golang world the phrases used are "forgotten sender" and 
"abandoned receiver" when goroutines "leak". This seems to lead to 
complicated recipes for signalling and shutdown. Java has had threads 
since JDK 1.0 and it doesn't seem to arise as often, one reason could be 
that you can keep a reference to a Thread, another might be that the 
serviceability/observability support is good so it is possible to 
identify orphaned threads and their stack traces, maybe reason about why 
they did not terminate when expected.

>
> But there are voices that garbage collection "alive" VT is 
> problematic: "Finalization is one topic. Another is Cleaner actions 
> that may run while a resource is in an inconsistent state." I don't 
> know this area so I can't deny it.
>
> I checked that the blocked task created by 
> Executors.newVirtualThreadPerTaskExecutor().submit() is GCed.

Considerable time was spent on this topic before making virtual threads 
a permanent feature in JDK 21. We decided to leave this as is for now. 
In the case of a Thread-Per-Task-Executor (TPTE) then it keeps a 
reference to all threads executing tasks. It has to because of 
shutdownNow or close that may have to interrupt all threads. A TPTE is 
typically stored somewhere, often a static final field, or it will be 
used with try-with-resources. So once a TPTE is strongly reachable then 
it will ensure that all virtual threads executing tasks in that executor 
are keep reachable. There is a similar story for other "unowned" 
executor service implementations. For now, unowned executor services are 
weakly tracked for observability purposes. There is an argument that 
they should be strongly tracked when there is at least one thread but 
that adds some bookkeeping overhead, esp. when oscillating between zero 
and a single thread. So everything you are probing here is possible 
future work.

-Alan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20240711/ddafdda8/attachment.htm>


More information about the loom-dev mailing list