Virtual threads created more platform threads
Jianbin Chen
jianbin at apache.org
Wed Jul 2 05:50:27 UTC 2025
Hi Liang,
Thank you and everyone for your patient answers. I verified this with JDK
24, and the same example did not show platform thread growth. However,
there's one thing I don't understand: why are these threads never
reclaimed? Most of them are in a waiting state - isn't this a waste of
resources? Sometimes it increases by hundreds of threads. Is there any way
to suppress this behavior on JDK 21?
Best Regards.
Jianbin Chen, github-id: funky-eyes
Chen Liang <liangchenblue at gmail.com> 于 2025年7月2日周三 12:58写道:
> Hello Jianbin, please provide a test case that can reproduce this issue on
> 24. 21 is not supported by active JDK development.
>
> On Tue, Jul 1, 2025, 23:52 Jianbin Chen <jianbin at apache.org> wrote:
>
>> Hi everyone,
>> thank you for your replies and assistance. So this issue occurring in JDK
>> 21 is considered normal behavior, correct? However, I've observed that
>> these expanded threads are not being garbage collected - they remain in a
>> waiting state for several minutes without disappearing. Sometimes they can
>> expand to several hundred threads, and these threads, once created, remain
>> unused, continuously wasting thread resources. Therefore, I'm attaching a
>> local reproduction example of this issue to ensure the information in this
>> email is complete.
>> -Djava.util.concurrent.ForkJoinPool.common.parallelism=1
>> `
>> The following example clearly shows the forkjoinpool expanding. I have
>> also recorded the runtime stack trace for this and submitted it as an
>> attachment.
>>
>> ```
>> public static void main(String[] args) throws InterruptedException {
>> Executor executor =
>> ThreadPoolFactory.newVirtualThreadPerTaskExecutor();
>> String a = "a";
>> executor.execute(() -> {
>> synchronized (a) {
>> try {
>> a.wait();
>> } catch (InterruptedException e) {
>> throw new RuntimeException(e);
>> }
>> }
>> });
>> executor.execute(() -> {
>> synchronized (a) {
>> try {
>> a.wait();
>> } catch (InterruptedException e) {
>> throw new RuntimeException(e);
>> }
>> }
>> });
>> Thread.sleep(120000);
>> }
>> ```
>> After switching to using condition, it can be clearly observed through
>> jstack that the forkjoinpool did not expand as many threads, but 3 threads
>> still appeared, which might be related to incorrect usage of my JVM
>> parameters.
>>
>> ```
>> public static void main(String[] args) throws InterruptedException {
>> Executor executor =
>> ThreadPoolFactory.newVirtualThreadPerTaskExecutor();
>> List<ReentrantLock> list = new ArrayList<>();
>> list.add(new ReentrantLock());
>> list.add(new ReentrantLock());
>> list.add(new ReentrantLock());
>> list.add(new ReentrantLock());
>> list.add(new ReentrantLock());
>> list.add(new ReentrantLock());
>> for (int i = 0; i < list.size(); i++) {
>> ReentrantLock value = list.get(i);
>> Condition condition = value.newCondition();
>> executor.execute(() -> {
>> value.lock();
>> try {
>> condition.await();
>> } catch (InterruptedException e) {
>> throw new RuntimeException(e);
>> }finally {
>> value.unlock();
>> }
>> });
>> }
>> Thread.sleep(120000);
>> }
>> ```
>>
>> Best Regards.
>> Jianbin Chen, github-id: funky-eyes
>>
>> Peter Eastham <petereastham at gmail.com> 于 2025年7月2日周三 12:16写道:
>>
>>> I hope to not create noise with my own comments, and I will concur with
>>> you that JEP 491 should mean this is resolved in Java 24, which Jianbin
>>> Chen should try out before and then alongside Robert's recommendation for
>>> creating a very simple reproduction.
>>>
>>> As Java 21 is still the current LTS, it isn't completely unreasonable to
>>> forward concerns to the mailing list. My understanding in this particular
>>> case is that JEP 491 is not going to be back ported to Java 21 as it has a
>>> dependency from a change in Java 23. (Potentially more, I can't remember
>>> the conversation completely, I only skimmed that email chain)
>>>
>>>
>>> Thanks,
>>> - Peter
>>>
>>> P.S. As I do a similar job, I'd like to call out that Vendors letting
>>> the occasional support question slip into here is a fine price to pay for
>>> the amount of questions they handle instead.
>>>
>>>
>>>
>>> On Tue, Jul 1, 2025, 9:17 PM Chen Liang <liangchenblue at gmail.com> wrote:
>>>
>>>> Hello, I don't think this would happen for JDK 24 - JEP 491 removed the
>>>> code that calls Blocker in Object.wait, which is exactly the goal of that
>>>> JEP.
>>>>
>>>> Note that virtual threads are still pinned when call stack goes into
>>>> native, as native execution may pass address to stack variables that will
>>>> be lost in context switches. In these cases, the traditional managed block
>>>> happens again.
>>>>
>>>> P.S. I personally think it is somewhat not responsible for JDK vendors
>>>> to ask users to upstream a question for an older JDK release that might no
>>>> longer apply on the latest release to a development-oriented mailing list.
>>>>
>>>> On Tue, Jul 1, 2025 at 9:47 PM Jianbin Chen <jianbin at apache.org> wrote:
>>>>
>>>>>
>>>>> Hi Loom-dev Community,
>>>>>
>>>>> I have a question about platform thread creation triggered by calling
>>>>> future.get() within virtual threads, and I would like to ask the community
>>>>> for assistance. The detailed information can be found in this issue:
>>>>> https://github.com/adoptium/adoptium-support/issues/1319. I hope to
>>>>> receive some help from the community regarding this matter. Thank you.
>>>>> Additionally, I'd like to know if this situation will still occur in
>>>>> JDK 24 and above?
>>>>>
>>>>> Best Regards.
>>>>> Jianbin Chen, github-id: funky-eyes
>>>>>
>>>>>
>>>>> Best Regards.
>>>>> Jianbin Chen, github-id: funky-eyes
>>>>>
>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20250702/1ae7fac4/attachment.htm>
More information about the loom-dev
mailing list