Java25 中虚拟线程仍然会被 pin 住的 3 种情况

Java juc About 10,415 words

JEP 491

JEP 491中解决了虚拟线程在synchronized锁内遇到阻塞时不能从载体线程上卸载的问题后,还列举了3种还没解决的场景。

但这些场景发生的概率很小,且都是编码可控。

解析类符号引用时因类加载阻塞

When resolving a symbolic reference (JVMS §5.4.3) to a class or interface and the virtual thread blocks while loading a class. This is a case where the virtual thread pins the carrier due to a native frame on the stack.

public class BlockingLoadClass {

    static {
        System.out.println("block load static code begin");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {

        }
        System.out.println("block load static code end");
    }

    public static void test() {
        System.out.println("test method");
    }

}

public class JavaVirtualThreadDemo {

    public static void main(String[] args) {
        Thread.startVirtualThread(() -> {
            System.out.println("invoke static method");
            BlockingLoadClass.test();
        });

        LockSupport.park();
    }

}

JFR查得Pinned Reason

VM call to com.center.BlockingLoadClass. on stack

PS \openjdk-25> jcmd 18480 JFR.view cell-height=20 jdk.VirtualThreadPinned
18480:

                                                  Virtual Thread Pinned

Start Time Duration Event Thread Stack Trace            Blocking Operation Pinned Reason          Carrier Thread
---------- -------- ------------ ---------------------- ------------------ ---------------------- ----------------------
20:21:51     20.9 s              com.center.Blocking... LockSupport.park   VM call to com.center. ForkJoinPool-1-worker-
                                 com.center.JavaVirt...                    BlockingLoadClass.<cli 1
                                 com.center.JavaVirt...                    nit> on stack
                                 java.lang.Thread.ru...
                                 java.lang.Thread.sl...
                                 java.lang.Thread.sl...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 jdk.internal.vm.Con...
                                 jdk.internal.vm.Con...
                                 jdk.internal.vm.Con...

Timespan: 20:12:15 - 20:22:15

类初始化器内阻塞

When blocking inside a class initializer. This is also a case where the virtual thread pins the carrier due to a native frame on the stack.

public class BlockingInitializationClass {

    private static final Integer VALUE = createValue();

    private static Integer createValue() {
        System.out.println("block load static code begin");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {

        }
        System.out.println("block load static code end");
        return 1;
    }

    public static Integer getValue() {
        System.out.println("test method");
        return VALUE;
    }

}

public class JavaVirtualThreadDemo {

    public static void main(String[] args) {
        Thread.startVirtualThread(() -> {
            System.out.println("invoke static method");
            BlockingInitializationClass.getValue();
        });

        LockSupport.park();
    }

}

JFR查得Pinned Reason

VM call to com.center.BlockingInitializationClass. on stack

PS \openjdk-25\bin> jcmd 18628 JFR.view cell-height=20 jdk.VirtualThreadPinned
18628:

                                                  Virtual Thread Pinned

Start Time Duration Event Thread Stack Trace            Blocking Operation Pinned Reason          Carrier Thread
---------- -------- ------------ ---------------------- ------------------ ---------------------- ----------------------
20:24:51     10.4 s              com.center.Blocking... LockSupport.park   VM call to com.center. ForkJoinPool-1-worker-
                                 com.center.Blocking...                    BlockingInitialization 1
                                 com.center.JavaVirt...                    Class.<clinit> on stac
                                 com.center.JavaVirt...                    k
                                 java.lang.Thread.ru...
                                 java.lang.Thread.sl...
                                 java.lang.Thread.sl...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 java.lang.VirtualTh...
                                 jdk.internal.vm.Con...
                                 jdk.internal.vm.Con...
                                 jdk.internal.vm.Con...

Timespan: 20:17:55 - 20:27:55

等待其他线程完成类初始化

When waiting for a class to be initialized by another thread (JVMS §5.5). This is a special case where the virtual thread blocks in the JVM, thus pinning the carrier.

public class BlockingLoadClass {

    static {
        System.out.println("block load static code begin " + Thread.currentThread());
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {

        }
        System.out.println("block load static code end " + Thread.currentThread());
    }

    public static void test() {
        System.out.println("test method: " + Thread.currentThread());
    }

}

public class JavaVirtualThreadDemo {

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            System.out.println(LocalDateTime.now() + " platform thread invoke static method");
            BlockingLoadClass.test();
        }).start();

        Thread.sleep(10000);

        Thread.startVirtualThread(() -> {
            System.out.println(LocalDateTime.now() + " virtual thread invoke static method");
            BlockingLoadClass.test();
        });

        LockSupport.park();
    }

}

JFR查得Pinned Reason

Waited for initialization of com.center.BlockingLoadClass by another thread

PS \openjdk-25> jcmd.exe 12420 JFR.view cell-height=20 jdk.VirtualThreadPinned
12420:

                                                  Virtual Thread Pinned

Start Time Duration Event Thread Stack Trace            Blocking Operation Pinned Reason          Carrier Thread
---------- -------- ------------ ---------------------- ------------------ ---------------------- ----------------------
20:16:49     10.4 s              com.center.JavaVirt... Object.wait        Waited for initializat ForkJoinPool-1-worker-
                                 com.center.JavaVirt...                    ion of com.center.Bloc 1
                                 java.lang.Thread.ru...                    kingLoadClass by anoth
                                 java.lang.VirtualTh...                    er thread
                                 java.lang.VirtualTh...
                                 jdk.internal.vm.Con...
                                 jdk.internal.vm.Con...

Timespan: 20:08:38 - 20:18:38

线程情况

工作线程挂载了虚拟线程,一直没有卸载。

可以看到jcmd命令的Thread.print功能中,会标识Carrying携带的虚拟线程、Mounted挂载的虚拟线程。

"ForkJoinPool-1-worker-1" #27 [5404] daemon prio=5 os_prio=0 cpu=0.00ms elapsed=15.27s tid=0x000001eb4e2e72d0  [0x0000001cf4afd000]
   Carrying virtual thread #26
        at jdk.internal.vm.Continuation.run(java.base@25/Continuation.java:251)
        at java.lang.VirtualThread.runContinuation(java.base@25/VirtualThread.java:293)
        at java.lang.VirtualThread$$Lambda/0x000000002700b890.run(java.base@25/Unknown Source)
        at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.compute(java.base@25/ForkJoinTask.java:1750)
        at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.compute(java.base@25/ForkJoinTask.java:1742)
        at java.util.concurrent.ForkJoinTask$InterruptibleTask.exec(java.base@25/ForkJoinTask.java:1659)
        at java.util.concurrent.ForkJoinTask.doExec(java.base@25/ForkJoinTask.java:511)
        at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(java.base@25/ForkJoinPool.java:1450)
        at java.util.concurrent.ForkJoinPool.runWorker(java.base@25/ForkJoinPool.java:2019)
        at java.util.concurrent.ForkJoinWorkerThread.run(java.base@25/ForkJoinWorkerThread.java:187)
   Mounted virtual thread #26
        at jdk.internal.misc.Unsafe.park(java.base@25/Native Method)
        at java.lang.VirtualThread.parkOnCarrierThread(java.base@25/VirtualThread.java:813)
        at java.lang.VirtualThread.park(java.base@25/VirtualThread.java:751)
        at java.lang.System$1.parkVirtualThread(java.base@25/System.java:2284)
        at java.util.concurrent.locks.LockSupport.park(java.base@25/LockSupport.java:367)
        at com.center.BlockingLoadClass.<clinit>(BlockingLoadClass.java:9)
        at com.center.JavaVirtualThreadDemo.lambda$main$0(JavaVirtualThreadDemo.java:10)
        at com.center.JavaVirtualThreadDemo$$Lambda/0x0000000027042a10.run(Unknown Source)
        at java.lang.Thread.runWith(java.base@25/Thread.java:1487)
        at java.lang.VirtualThread.run(java.base@25/VirtualThread.java:456)
        at java.lang.VirtualThread$VThreadContinuation$1.run(java.base@25/VirtualThread.java:248)
        at jdk.internal.vm.Continuation.enter0(java.base@25/Continuation.java:325)
        at jdk.internal.vm.Continuation.enter(java.base@25/Continuation.java:316)

pinned threads

使用jcmd中的JFRjfr命令查看被pin住的线程。

PS \openjdk-25> jcmd 7316 JFR.view verbose=true pinned-threads
7316:

                                Pinned Virtual Threads

Method                                  Pinned Count Longest Pinning Total Time Pinned
(stackTrace.topApplicationFrame)         (startTime)      (duration)        (duration)
--------------------------------------- ------------ --------------- -----------------
com.center.BlockingLoadClass.<clinit>()            1          10.4 s            10.4 s

COLUMN 'Method', 'Pinned Count', 'Longest Pinning', 'Total Time Pinned' SELECT
stackTrace.topApplicationFrame AS S, COUNT(*), MAXIMUM(duration), SUM(duration) AS T
FROM VirtualThreadPinned GROUP BY S ORDER BY T DESC

Execution: query-validation=0 s, aggregation=58.0 ms, formatting=10.0 ms


Timespan: 17:49:06 - 17:59:06

官方文档

https://openjdk.org/jeps/491

Views: 3 · Posted: 2025-12-28

———         Thanks for Reading         ———

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

扫描下方二维码关注公众号和小程序↓↓↓

扫描下方二维码关注公众号和小程序↓↓↓
Prev Post
Today In History
Browsing Refresh