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

Alan Bateman Alan.Bateman at oracle.com
Thu Jul 25 12:54:59 UTC 2024



On 25/07/2024 13:21, Matthew Swift wrote:
> Hmm, I'm starting to think I may have fallen into the same trap here 
> as Michal.
>
> I've been using virtual threads similar to platform threads for 
> performing IO tasks asynchronously.
>
> Background: originally, I was using 
> Executors#newVirtualThreadPerTaskExecutor to run these tasks, which 
> was fine because the Executor tracks the virtual threads it creates in 
> order to support shutdown. However, from an observability point of 
> view I've found the executor to be a bit frustrating because it is 
> impossible to set the thread name before the thread is scheduled to 
> run[1].
You can specify a ThreadFactory to newThreadPerTaskExecutor so you can 
set the thread name if you need it. Virtual threads are meant to be 
lightweight and numerous so don't get a name by default. For many cases, 
this is okay as the they have a thread ID which will identity them in 
thread dumps and elsewhere.


> This means that under heavy load where the FJ pool is busy a thread 
> dump shows many unnamed threads, which are waiting to be scheduled for 
> the first time. Admittedly, this is only a minor annoyance because I'm 
> more interested in the threads that are clogging up the FJ pool than 
> the ones which are waiting to use it, even so it'd be nice to have an 
> overall picture of what's active and what's queued (note also thread 
> names are included in JFR events, which is super helpful).

With the Loom EA builds [1] you can use `jcmd <pid> 
Thread.vthread_summary` to get a summary view of all thread grouping so 
you get thread counts, a summary of the scheduler, timers, and any 
socket I/O that it outstanding. We would like to bring this diagnostic 
command into the main line JDK at some point.

>
> To remedy this, I've switched away from using an Executor and now I 
> just use "Thread.ofVirtual().name(initialName).start(task)". However, 
> I don't think all of the tasks are strongly reachable - some are "fire 
> and forget" tasks (e.g. async resource cleanup), so I may be 
> inadvertently relying on the JVM's observability support to keep these 
> tasks alive until they complete, which seems a bit brittle. In fact, 
> now that I think of it, it may not even be limited to fire and forget 
> tasks. A VT that is reading messages from a Socket could be GC'd
A virtual thread that is blocked reading from a Socket will continue 
when there are bytes to read, the peer closes the connection, some I/O 
exception, or the thread is interrupted. So it will be strongly reachable.

-Alan

[1] https://jdk.java.net/loom/


More information about the loom-dev mailing list