jstack 命令检查 Spring MVC 应用启动时的各个线程

  • Post author:
  • Post category:其他




背景

最近学习 Java 线程相关知识,接触到了

jstack

这个命令,挺有意思的。下面动手试试。



jstack 是什么?

简单来说就是,把 java 当前各个线程相关状态打印出来。

实际使用时,首先借助

jps

命令找到希望观察的 java 进程 ID,然后执行

jstack

+ 查出来的进程 ID 来查看该进程的线程状态。



使用 jstack 查看 Spring Boot 启动时线程

手头正好有一个公司的 Spring Boot 项目,把它启动了,然后执行

jps

命令:

$ jps
1992 **Application // 这个就是 Spring Boot 应用的名称(打了星号)
2792
8488 Jps
12028 RemoteMavenServer36
13164
3676 Launcher

既然知道了 Spring Boot 应用的进程 ID,接下来对该进程执行

jstack

命令:

$ jstack 1992
2021-07-24 14:10:34
Full thread dump OpenJDK 64-Bit Server VM (25.292-b10 mixed mode):
// 输出内容很长,下面分组研究



【2】Druid 数据库连接线程

在打印内容中可以看到,第一部分是两个 Druid 数据库连接线程:

"Druid-ConnectionPool-Destroy-566039179" #88 daemon prio=5 os_prio=0 tid=0x000001f19c6e9000 nid=0x2e74 waiting on condition [0x000000861c6fe000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at com.alibaba.druid.pool.DruidDataSource$DestroyConnectionThread.run(DruidDataSource.java:2540)

"Druid-ConnectionPool-Create-566039179" #87 daemon prio=5 os_prio=0 tid=0x000001f19c6ee000 nid=0x1828 waiting on condition [0x000000861c5ff000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000077a9afe10> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2443)



【1】MySQL Statement Cancellation Timer 线程

接着是一个相当奇怪的“MySQL 语句取消计时器”:

"MySQL Statement Cancellation Timer" #86 daemon prio=5 os_prio=0 tid=0x000001f19c6e5000 nid=0x778 in Object.wait() [0x000000861c4ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076d266448> (a java.util.TaskQueue)
        at java.lang.Object.wait(Object.java:502)
        at java.util.TimerThread.mainLoop(Timer.java:526)
        - locked <0x000000076d266448> (a java.util.TaskQueue)
        at java.util.TimerThread.run(Timer.java:505)



【6】RMI 相关线程

查了一下,

RMI



Java Remote Method Invocation

,相当于 Java 内置的 RPC 吧。



【5】RMI TCP 连接线程

然后是 5 个

RMI TCP

连接线程,中间隔了很远。

但是我不太明白,只是启动一个 Spring Boot,怎么就涉及 RMI 了?

"RMI TCP Connection(7)-172.27.80.1" #83 daemon prio=5 os_prio=0 tid=0x000001f19c6eb800 nid=0x1008 runnable [0x000000861c2fe000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
 		# 中间略
 		at java.lang.Thread.run(Thread.java:748)

"RMI TCP Connection(6)-172.27.80.1" #80 daemon prio=5 os_prio=0 tid=0x000001f19c6ec800 nid=0x3e38 runnable [0x000000861c1fe000]
   java.lang.Thread.State: RUNNABLE
   		at java.net.SocketInputStream.socketRead0(Native Method)
   		# 中间略
   		at java.lang.Thread.run(Thread.java:748)
   		
   		
# 中间隔了很多很多别的线程


"RMI TCP Connection(5)-172.27.80.1" #30 daemon prio=5 os_prio=0 tid=0x000001f19938a800 nid=0x1df4 runnable [0x0000008619afd000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        # 中间略
        at java.lang.Thread.run(Thread.java:748)

"RMI TCP Connection(4)-172.27.80.1" #28 daemon prio=5 os_prio=0 tid=0x000001f1997f1800 nid=0x32dc runnable [0x00000086199fe000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        # 中间略
        at java.lang.Thread.run(Thread.java:748)
"RMI TCP Connection(2)-172.27.80.1" #16 daemon prio=5 os_prio=0 tid=0x000001f19a009000 nid=0x11d8 runnable [0x0000008619bfe000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        # 中间略
        at java.lang.Thread.run(Thread.java:748)

还有,这里的

172.27.80.1

是谁?

查了一下,是

Ethernet adapter vEthernet (Default Switch)

Ethernet adapter vEthernet (Default Switch):

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::c97e:8317:192b:14ae%54
   IPv4 Address. . . . . . . . . . . : 172.27.80.1
   Subnet Mask . . . . . . . . . . . : 255.255.240.0
   Default Gateway . . . . . . . . . :



【1】RMI 调度线程

在很靠后的位置有个

RMI

调度线程:

"RMI Scheduler(0)" #17 daemon prio=5 os_prio=0 tid=0x000001f19a014000 nid=0x6f0 waiting on condition [0x0000008619cfe000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006c2939c20> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        # 中间略
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)



【1】RMI 接受线程

最后面有个

RMI

接受线程:

"RMI TCP Accept-0" #13 daemon prio=5 os_prio=0 tid=0x000001f199965800 nid=0x287c runnable [0x00000086197ff000]
   java.lang.Thread.State: RUNNABLE
        at java.net.DualStackPlainSocketImpl.accept0(Native Method)
        at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:131)
        at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
        at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
        - locked <0x00000006c293a3a8> (a java.net.SocksSocketImpl)
        at java.net.ServerSocket.implAccept(ServerSocket.java:560)
        at java.net.ServerSocket.accept(ServerSocket.java:528)
        at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:52)
        at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:405)
        at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:377)
        at java.lang.Thread.run(Thread.java:748)



【1】DestroyJavaVM 线程

然后是一个

DestroyJavaVM

线程。

简单来说,这个线程什么都不干,就是等着其他非守护线程的活儿都干完了(

join

),就关闭 Java 虚拟机。

参考链接:

DestroyJavaVM thread ALWAYS running

"DestroyJavaVM" #75 prio=5 os_prio=0 tid=0x000001f19c6e6000 nid=0x3d08 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE



【14】端口 8080 的 HTTP 相关线程

接下来是 HTTP 相关的三类线程。

8080

端口是负责向用户提供服务的。

参考链接:

Tomcat NIO线程模型深入分析



【1】HTTP NIO Acceptor 线程

Acceptor 线程比较好理解,应该是专门负责接受请求:

"http-nio-8080-Acceptor-0" #73 daemon prio=5 os_prio=0 tid=0x000001f19c6e8800 nid=0x3a28 runnable [0x000000861c0fe000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
        at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:421)
        at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:249)
        - locked <0x000000077a5c2618> (a java.lang.Object)
        at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:448)
        at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:70)
        at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:95)
        at java.lang.Thread.run(Thread.java:748)



【2】HTTP NIO ClientPoller 线程

接下来是两个 poller 线程。

简单来说就是负责观察,看前面被 acceptor 线程接受请求后创建的 channel,哪个可读/可写了,就派执行线程去处理。

前一阵刚学到,这叫 IO 多路复用模型,但我总是愿意简单理解为“事件驱动”。简单来说就是,有事儿的时候再干活儿,没事儿的时候歇着。也不是说这种模型多合理,这太正常了,是之前的模型太不合理了,没事儿的时候也要阻塞。

"http-nio-8080-ClientPoller-1" #72 daemon prio=5 os_prio=0 tid=0x000001f19c6e4800 nid=0x1f4c runnable [0x000000861bffe000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
        # 中间略
        at java.lang.Thread.run(Thread.java:748)

"http-nio-8080-ClientPoller-0" #71 daemon prio=5 os_prio=0 tid=0x000001f19c4dc800 nid=0x293c runnable [0x000000861befe000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
        # 中间略
        at java.lang.Thread.run(Thread.java:748)



【10】HTTP NIO 执行线程

这就是真正干活儿的线程了,一共 10 个,可以理解为一个线程池。

这里省去中间的,放上首尾两个线程:

"http-nio-8080-exec-10" #70 daemon prio=5 os_prio=0 tid=0x000001f19c4db800 nid=0x1140 waiting on condition [0x000000861bdff000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000077a584018> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
        
# 中间略

"http-nio-8080-exec-1" #61 daemon prio=5 os_prio=0 tid=0x000001f19c4d0000 nid=0x10f8 waiting on condition [0x000000861b4ff000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000077a584018> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)



【1】BlockPoller 线程

说实话,我没太明白这个跟上面的 ClientPoller 有啥区别:

"NioBlockingSelector.BlockPoller-2" #60 daemon prio=5 os_prio=0 tid=0x000001f19c4d3000 nid=0x219c runnable [0x000000861b3fe000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:314)
        at sun.nio.ch.WindowsSelectorImpl$SubSelector.access$400(WindowsSelectorImpl.java:293)
        at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:174)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x000000077a586688> (a sun.nio.ch.Util$3)
        - locked <0x000000077a586678> (a java.util.Collections$UnmodifiableSet)
        - locked <0x000000077a586508> (a sun.nio.ch.WindowsSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at org.apache.tomcat.util.net.NioBlockingSelector$BlockPoller.run(NioBlockingSelector.java:304)



【1】调度线程

调度线程只有一个,这很合理。应该就是它在决定哪些线程获得当前的 CPU 时间了吧!

注意,这是目前第一个“非守护线程”,没有标注

daemon

"scheduling-1" #59 prio=5 os_prio=0 tid=0x000001f19c4d7000 nid=0x38b8 waiting on condition [0x000000861b2ff000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000077ad451e0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)



【14】端口8293 的 HTTP 相关线程

这个接口是用来提供内部管理服务的。在

application.yml

中:

management:
  server:
    port: 8293

这 14 个线程和上面

8080

端口的 14 个线程类似,这里就不展开了。



【2】container 线程

接下来是一个

container

线程,然后隔了几个其他线程,又来了另一个

container

线程。

注意,它们也是非守护线程。干什么用的目前还没搞懂,应该是和

tomcat

有关。

"container-1" #43 prio=5 os_prio=0 tid=0x000001f19c3a1800 nid=0x1104 waiting on condition [0x000000861a3ff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.catalina.core.StandardServer.await(StandardServer.java:568)
        at org.springframework.boot.web.embedded.tomcat.TomcatWebServer$1.run(TomcatWebServer.java:181)

# 中间掺杂了其他线程

"container-0" #34 prio=5 os_prio=0 tid=0x000001f19a843800 nid=0x1b20 waiting on condition [0x000000861a0ff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.catalina.core.StandardServer.await(StandardServer.java:568)
        at org.springframework.boot.web.embedded.tomcat.TomcatWebServer$1.run(TomcatWebServer.java:181)



【3】Catalina-utility 线程

然后是 4 个 Catalina-utility 线程。没挨在一起不说,奇怪的是,有两个 1 号、两个 2 号。

这个也不是守护进程,目前还没搞清楚是干嘛的。

"Catalina-utility-2" #42 prio=1 os_prio=-2 tid=0x000001f19c3a5800 nid=0x600 waiting on condition [0x000000861a2fe000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000077ac6aef0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        # 中间略
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

"Catalina-utility-1" #41 prio=1 os_prio=-2 tid=0x000001f19c3a0800 nid=0x2bc4 waiting on condition [0x000000861a1ff000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000077ac6aef0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        # 中间略
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

(中间夹杂了别的线程)

"Catalina-utility-2" #33 prio=1 os_prio=-2 tid=0x000001f19a843000 nid=0x367c waiting on condition [0x0000008619fff000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000077a5e42c0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        # 中间略
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

"Catalina-utility-1" #32 prio=1 os_prio=-2 tid=0x000001f19a5ce000 nid=0x270c waiting on condition [0x0000008619efe000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000077a5e42c0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        # 中间略
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)



【1】Log4j 日志线程

接着看到了熟悉的

Log4j

线程!

之前 Debug 的时候就注意到过,

Log4j

打日志不是同步的,有的时候打印日志的语句过去好几条都没打,然后又一下子打出来好几条。。

原来是用了一个单独的线程,异步来打印日志呀!这下明白了。

这个是守护线程,非常合理,就是来帮忙的嘛:

"Log4j2-TF-6-AsyncLoggerConfig-2" #19 daemon prio=5 os_prio=0 tid=0x000001f19a2a5000 nid=0x26e4 waiting on condition [0x0000008619dff000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006c27729a8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        at com.lmax.disruptor.TimeoutBlockingWaitStrategy.waitFor(TimeoutBlockingWaitStrategy.java:38)
        at com.lmax.disruptor.ProcessingSequenceBarrier.waitFor(ProcessingSequenceBarrier.java:56)
        at com.lmax.disruptor.BatchEventProcessor.processEvents(BatchEventProcessor.java:159)
        at com.lmax.disruptor.BatchEventProcessor.run(BatchEventProcessor.java:125)
        at java.lang.Thread.run(Thread.java:748)



【1】Service 线程

暂时没搞明白,总之是个守护线程:

"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000001f1996e1800 nid=0x270 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE



【4】编译器线程

看来这就是大名鼎鼎的 JIT 编译器了!编译器线程会在应用运行的过程中,把用到多的部分编译为机器码,以提高性能。

我用的是

corretto-1.8

JRE,编译器线程的数量可能和这个有关系吧:

"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000001f199667800 nid=0xc94 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000001f199655800 nid=0x29b8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000001f199655000 nid=0x3c10 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000001f199653800 nid=0x1f0c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE



【1】中断线程

什么。。这也是个线程??专门用来监听

Ctrl C

吗?。。

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000001f1995d9800 nid=0x21f4 runnable [0x00000086191fe000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x00000006c29705c0> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x00000006c29705c0> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:48)



【1】Attach 线程

似乎是帮助 Debug 用的一个守护线程:

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000001f1972d8800 nid=0x30d0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

参考链接:

Understanding JVM’s “Attach Listener” thread



【1】信号分派线程

按字面理解,分派信号的一个守护线程:

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000001f197295000 nid=0x41c runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE



【1】Finalizer 线程

这个应该是垃圾回收时,专门用来调用被回收对象的

finalize

方法的守护线程:

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000001f197276800 nid=0x12d8 in Object.wait() [0x0000008618eff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000006c2972a20> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000006c2972a20> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

参考链接:

what will the Finalizer thread do if there is a infinite loop or deadlock in the Java finalize method



【1】Reference Handler 线程

暂时不知道是干嘛的,推测和垃圾回收有关:

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000001f1fc0d5000 nid=0xf08 in Object.wait() [0x0000008618dff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000006c2985e78> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000006c2985e78> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)



【1】VM 线程

似乎也和垃圾回收等操作有很大关系,而且

jstack

命令很可能也是借助这个线程实现的。奇怪的是,这个居然不是守护线程:

"VM Thread" os_prio=2 tid=0x000001f197250000 nid=0x60 runnable

参考链接:

What does java “VM thread” do?



【8】垃圾回收线程

顾名思义,用来进行垃圾回收的线程(居然不是守护线程??):

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000001f1fc071800 nid=0x29d8 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000001f1fc073000 nid=0x19f0 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000001f1fc074800 nid=0x3f4c runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000001f1fc075800 nid=0x39b0 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000001f1fc079000 nid=0x2194 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x000001f1fc07a000 nid=0x3bc runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x000001f1fc07d000 nid=0x8ec runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x000001f1fc07e000 nid=0x3d20 runnable



【1】VM 定期任务线程

似乎是用来对 JVM 进行采样和性能分析的:

"VM Periodic Task Thread" os_prio=2 tid=0x000001f199969800 nid=0x1044 waiting on condition

参考链接:

What is the “VM Periodic Task Thread”?



JNI global references

在输出的最后,还有这样一行:

JNI global references: 1504

虽然不是一个线程吧。



小结

没想到简单跑一个 Spring Boot 应用,就出来这么多线程!

之后有时间的话可以好好研究一下,分分类,继续更新吧。



版权声明:本文为VoisSurTonChemin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。