JDK 21 中对虚拟线程的 DDR 支持

0.前言

DDR(直接转储读取器)是 DTFJ(Java 诊断工具框架)API 的 Java 实现。它通过遍历转储中的 J9 结构来提取虚拟机和应用程序状态。它对于在程序执行的某个点检查 Java 对象很有用。例如,您可以列出所有线程并检查线程的堆栈跟踪。

在 Java 19 中,Project Loom 引入了虚拟线程。虚拟线程是一种轻量级实现,java.lang.Thread允许人们编写高度并发的应用程序。与平台线程相比,虚拟线程不与任何操作系统线程绑定,而是挂载在平台线程上。如果遇到阻塞操作,则可以卸载虚拟线程,然后其继续不再与任何操作系统线程相关联。

随着越来越多的应用程序使用虚拟线程,在 DDR 中支持虚拟线程对于调试具有虚拟线程的程序来说将变得至关重要。为了支持虚拟线程,添加了三个新的 DDR 命令:vthreads和continuationstack。continuationstackslots

1.一个例子

我们将通过以下示例介绍新命令:

public static void main(String[] args) throws InterruptedException {
    Thread t1 = Thread.ofVirtual().name("vthread 1").start(() -> {
        System.out.println("virtual thread 1 starts");
        LockSupport.park();
        System.out.println("virtual thread 1 ends");
    });

    while (t1.getState() != Thread.State.WAITING) {
        Thread.sleep(500); // let vthread 1 park
    }

    Thread t2 = Thread.ofVirtual().name("vthread 2").start(() -> {
        System.out.println("virtual thread 2 starts");
        com.ibm.jvm.Dump.systemDumpToFile();
        LockSupport.unpark(t1);
        System.out.println("virtual thread 2 ends");
    });

    t1.join();
    t2.join();
}

在示例中,创建了两个虚拟线程,其中一个处于暂停状态,因此被卸载。

为了获取所需的核心文件,您可能需要使用选项运行示例-Xdump:system:request=exclusive+prepwalk。

2.vthreads​命令

该vthreads命令列出了核心文件中的所有虚拟线程。

让我们用 打开生成的核心文件,并通过键入来jdmpview调用命令,我们得到输出:vthreads!vthreads

> !vthreads
!continuationstack 0x00007f1d94003410 !j9vmcontinuation 0x00007f1d94003410 !j9object 0x00000007FFF9A848 (Continuation) !j9object 0x00000007FFF9A7A0 (VThread) - vthread 2
!continuationstack 0x00007f1d94010110 !j9vmcontinuation 0x00007f1d94010110 !j9object 0x00000007FFF8C598 (Continuation) !j9object 0x00000007FFF8C490 (VThread) - vthread 1

每行列出了四个命令:

  • 检查延续的堆栈跟踪
  • 检查本机延续结构
  • 检查延续对象
  • 检查虚拟线程对象
  • 虚拟线程的名称也会显示在行末(如果设置了名称)。

该vthreads命令通常是打开核心文件后要执行的第一步。您可以通过查看名称或检查堆栈跟踪(我们将在下一节中介绍)来识别要处理的虚拟线程。

和命令continuationstack​continuationstackslots
堆栈跟踪对于在崩溃后或在程序执行的某个点快速获取诊断信息很有用。该continuationstack命令输出延续的堆栈跟踪。运行!continuationstack 0x00007f1d94003410(虚拟线程 2 的延续)将输出:

> !continuationstack 0x00007f1d94003410
<7f1d94003410>  !j9method 0x00000000000A58F0   jdk/internal/vm/Continuation.enterImpl()Z
<7f1d94003410>  !j9method 0x00000000000A5770   jdk/internal/vm/Continuation.run()V
<7f1d94003410>  !j9method 0x0000000000096118   java/lang/VirtualThread.runContinuation()V
<7f1d94003410>  !j9method 0x000000000026D388   java/lang/VirtualThread$$Lambda/0x0000000000000000.run()V
<7f1d94003410>  !j9method 0x000000000026F388   java/util/concurrent/ForkJoinTask$RunnableExecuteAction.exec()Z
<7f1d94003410>  !j9method 0x000000000026E108   java/util/concurrent/ForkJoinTask.doExec()I
<7f1d94003410>  !j9method 0x000000000025E440   java/util/concurrent/ForkJoinPool$WorkQueue.topLevelExec(Ljava/util/concurrent/ForkJoinTask;Ljava/util/concurrent/ForkJoinPool$WorkQueue;)V
<7f1d94003410>  !j9method 0x000000000025BC10   java/util/concurrent/ForkJoinPool.scan(Ljava/util/concurrent/ForkJoinPool$WorkQueue;II)I
<7f1d94003410>  !j9method 0x000000000025BBF0   java/util/concurrent/ForkJoinPool.runWorker(Ljava/util/concurrent/ForkJoinPool$WorkQueue;)V
<7f1d94003410>  !j9method 0x00000000001FFF30   java/util/concurrent/ForkJoinWorkerThread.run()V
<7f1d94003410>                          JNI call-in frame
<7f1d94003410>                          Native method frame

请注意,这实际上是载体线程的堆栈跟踪。这是因为虚拟线程 2 已挂载,并且在挂载过程中,延续的某些变量(例如堆栈指针)与载体线程的变量进行了交换。因此,该命令将continuationstack输出已挂载虚拟线程的载体线程的堆栈跟踪。要查看虚拟线程的堆栈跟踪,请使用stack载体线程的命令:

> !stack 0x0023d700
<23d700>        !j9method 0x0000000000286A20   com/ibm/jvm/Dump.triggerDumpsImpl(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
<23d700>        !j9method 0x00000000002868C0   com/ibm/jvm/Dump.triggerDump(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
<23d700>        !j9method 0x00000000002867C0   com/ibm/jvm/Dump.systemDumpToFile()Ljava/lang/String;
<23d700>        !j9method 0x0000000000246800   VTTest2.lambda$main$1(Ljava/lang/Thread;)V
<23d700>        !j9method 0x0000000000246DE8   VTTest2$$Lambda/0x000000001436ed78.run()V
<23d700>        !j9method 0x00000000000961D8   java/lang/VirtualThread.runWith(Ljava/lang/Object;Ljava/lang/Runnable;)V
<23d700>        !j9method 0x00000000000961B8   java/lang/VirtualThread.run(Ljava/lang/Runnable;)V
<23d700>        !j9method 0x000000000026C770   java/lang/VirtualThread$VThreadContinuation$1.run()V
<23d700>        !j9method 0x00000000000A5750   jdk/internal/vm/Continuation.enter(Ljdk/internal/vm/Continuation;)V
<23d700>                                JNI call-in frame
<23d700>                                Native method frame

对于未挂载的虚拟线程,该continuationstack命令将输出虚拟线程的堆栈跟踪:

> !continuationstack 0x00007f1d94010110
<7f1d94010110>  !j9method 0x00000000000A5910   jdk/internal/vm/Continuation.yieldImpl(Z)Z
<7f1d94010110>  !j9method 0x00000000000A57B0   jdk/internal/vm/Continuation.yield0()Z
<7f1d94010110>  !j9method 0x00000000000A5790   jdk/internal/vm/Continuation.yield(Ljdk/internal/vm/ContinuationScope;)Z
<7f1d94010110>  !j9method 0x0000000000096298   java/lang/VirtualThread.yieldContinuation()Z
<7f1d94010110>  !j9method 0x0000000000096378   java/lang/VirtualThread.park()V
<7f1d94010110>  !j9method 0x00000000000B9198   java/lang/Access.parkVirtualThread()V
<7f1d94010110>  !j9method 0x0000000000278110   jdk/internal/misc/VirtualThreads.park()V
<7f1d94010110>  !j9method 0x000000000008F2C8   java/util/concurrent/locks/LockSupport.park()V
<7f1d94010110>  !j9method 0x0000000000246820   VTTest2.lambda$main$0()V
<7f1d94010110>  !j9method 0x0000000000246AE8   VTTest2$$Lambda/0x000000001436ec28.run()V
<7f1d94010110>  !j9method 0x00000000000961D8   java/lang/VirtualThread.runWith(Ljava/lang/Object;Ljava/lang/Runnable;)V
<7f1d94010110>  !j9method 0x00000000000961B8   java/lang/VirtualThread.run(Ljava/lang/Runnable;)V
<7f1d94010110>  !j9method 0x000000000026C770   java/lang/VirtualThread$VThreadContinuation$1.run()V
<7f1d94010110>  !j9method 0x00000000000A5750   jdk/internal/vm/Continuation.enter(Ljdk/internal/vm/Continuation;)V
<7f1d94010110>                          JNI call-in frame
<7f1d94010110>                          Native method frame

stack此外,与和命令类似stackslots,我们还有continuationstackslots随堆栈跟踪一起输出插槽的命令。

相关推荐

  1. JDK 21 虚拟线 DDR 支持

    2024-03-31 22:34:02       16 阅读
  2. 浅谈线理解

    2024-03-31 22:34:02       15 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-31 22:34:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-31 22:34:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-31 22:34:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-31 22:34:02       20 阅读

热门阅读

  1. 5.94 BCC工具之cachetop.py解读

    2024-03-31 22:34:02       19 阅读
  2. 怎么使用Redis模拟Session

    2024-03-31 22:34:02       17 阅读
  3. DDPM pytorch代码详细注释

    2024-03-31 22:34:02       18 阅读
  4. 学习笔记之嵌入式八股文(C语言)

    2024-03-31 22:34:02       15 阅读
  5. 2024.2.3力扣每日一题——石子游戏7

    2024-03-31 22:34:02       16 阅读
  6. 6 字符串、元组和字典

    2024-03-31 22:34:02       15 阅读
  7. Unity 通过鼠标移动和LineRenderer组件实现画线功能

    2024-03-31 22:34:02       16 阅读
  8. stm32通过串口发送float数据的方法

    2024-03-31 22:34:02       15 阅读