[External] : Re: Virtual thread memory leak because TrackingRootContainer keeps threads
Michal Domagala
outsider404 at gmail.com
Tue Jul 9 15:24:50 UTC 2024
Indeed, for 25+ years thread is GC root.
But for 3+ years virtual threads are present and they are documented as
ephemeral.
Developers are used to alive threads and virtual, alive threads are not
confusing. But virtual, alive threads documented as ephemeral are confusing.
Moreover, the idea that VT from Executors API is GCed but from Thread API
not looks inconsistent. But it is low confusion. Working against
documentation is high confusion.
Let's consider my case. I take 1000 Kafka events for concrete people each
poll. 90% of events are ignored (not my customers), 10% are processed (my
customers), but each event must be acked.
Each customer has a blocking queue. 10% is distributed to proper queues.
Each queue has a VT, which processes the event and finally ack the event.
>From time to time customers are off. Their blocking queues are thrown away.
There is a chance that at the same moment the customer is off, new poll
come with customer events. Kafka listener put event to customer queue and
expects VT will ack the event.
VT can't be stopped by the "customer-off" process, because new events may
come to the blocking queue anytime.
Race can be solved without synchronization. GC "knows" when the blocking
queue is not visible by Kafka listener thread and "stops" the VT by
reclaiming its resources.
I hope this example is clear enough to see that reclaiming VT resources by
GC is simple and automatic when manual solution requires thread
synchronization and is prone to errors.
wt., 9 lip 2024 o 16:24 Alan Bateman <Alan.Bateman at oracle.com> napisał(a):
> On 09/07/2024 12:51, Michal Domagala wrote:
> > Thanks for valuable observability context
> >
> > As I understand, initially VT created by Thread API was not observable
> > in thread dump and VT created by Executors API was observable. It was
> > confusing and now both kinds of VT are observable. Make sense.
> > But observability is reached by strong reference. VT created by Thread
> > API are strongly referenced until stop and not GC'ed until stop and VT
> > created by Executors API are not referenced at all and GC'ed when not
> > reachable, regardless of being stopped or not. That's not OK and much
> > worse than previous confusion.
>
> An ExecutorService implementation has to keep a reference to each
> task/thread because of the shutdownNow and close method. There are other
> thread groupings that arise when using structured concurrency that
> require a thread executing a main task wait for threads executing
> subtasks to finish executing.
>
> As things stand I don't expect there will be much confusion. For 25+
> years, a started thread is alive (and strongly reachable) until it
> terminates. Having virtual thread work mostly the same shouldn't be a
> surprise. As I said, the door hasn't been closed on introducing some
> notion of ephemeral thread in the future. It's not really feasible right
> now because of finalization and because of several issues related to
> cleaner actions and explicit APIs to keep some object reachable until a
> thread gets to some point in its execution. So there is more to this
> than just observability (and yes of course observability could traverse
> other reference types if needed).
>
> As regards the thread counting in the root container when running with
> the undocumented system property jdk.trackAllThreads is set to false.
> That is the count of the live (started but not terminated) threads in
> that thread grouping. It is a left over from when we will mulling over
> what the right default should be (reporting a thread count when you
> can't list the threads has some value).
>
> -Alan
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20240709/99016301/attachment.htm>
More information about the loom-dev
mailing list