[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