深入jvm-2
hello world程序会启动几个线程?
mkdir hello-world
cd hello-world
gradle init --type java-application
cat src/main/java/App.java
/*
* This Java source file was generated by the Gradle 'init' task.
*/
public class App {
public String getGreeting() {
return "Hello world.";
}
public static void main(String[] args) throws Exception {
System.out.println(new App().getGreeting());
while (true) {
// hang here for the purpose of debug
}
}
}
基础环境
# java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
# gradle -v
------------------------------------------------------------
Gradle 4.8
------------------------------------------------------------
Build time: 2018-06-04 10:39:58 UTC
Revision: 9e1261240e412cbf61a5e3a5ab734f232b2f887d
Groovy: 2.4.12
Ant: Apache Ant(TM) version 1.9.11 compiled on March 23 2018
JVM: 1.8.0_161 (Oracle Corporation 25.161-b12)
OS: Linux 4.9.0-6-amd64 amd64
编译/执行
gradle run
jstack结果
# jstack ${pid}
2018-07-22 11:33:53
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode):
"Attach Listener" #9 daemon prio=9 os_prio=0 tid=0x00007f8620001000 nid=0x1d8e waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Service Thread" #8 daemon prio=9 os_prio=0 tid=0x00007f865c0c9800 nid=0x1d5d runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f865c0be800 nid=0x1d5c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f865c0bd000 nid=0x1d5b waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f865c0ba000 nid=0x1d5a waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f865c0b8800 nid=0x1d59 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f865c085800 nid=0x1d58 in Object.wait() [0x00007f86472f1000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076df88ec0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x000000076df88ec0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f865c081000 nid=0x1d57 in Object.wait() [0x00007f86473f2000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076df86b68> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076df86b68> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=0 tid=0x00007f865c00b800 nid=0x1d4f runnable [0x00007f8664c3c000]
java.lang.Thread.State: RUNNABLE
at App.main(App.java:12)
"VM Thread" os_prio=0 tid=0x00007f865c079800 nid=0x1d56 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f865c021000 nid=0x1d52 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f865c023000 nid=0x1d53 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f865c024800 nid=0x1d54 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f865c026800 nid=0x1d55 runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f865c0ce800 nid=0x1d5e waiting on condition
JNI global references: 6
pstree结果
# pstree -p ${pid}
java---15*[{java}]
pidstat结果
# pidstat -t -p ${pid}
Linux 4.9.0-6-amd64 (concurrent.work) 2018年07月22日 _x86_64_ (4 CPU)
11时36分57秒 UID TGID TID %usr %system %guest %wait %CPU CPU Command
11时36分57秒 1000 7501 - 29.80 0.01 0.00 0.00 29.81 1 java
11时36分57秒 1000 - 7501 0.00 0.00 0.00 0.00 0.00 1 |__java
11时36分57秒 1000 - 7503 29.80 0.01 0.00 0.01 29.80 0 |__java
11时36分57秒 1000 - 7506 0.00 0.00 0.00 0.00 0.00 2 |__java
11时36分57秒 1000 - 7507 0.00 0.00 0.00 0.00 0.00 1 |__java
11时36分57秒 1000 - 7508 0.00 0.00 0.00 0.00 0.00 2 |__java
11时36分57秒 1000 - 7509 0.00 0.00 0.00 0.00 0.00 2 |__java
11时36分57秒 1000 - 7510 0.00 0.00 0.00 0.00 0.00 2 |__java
11时36分57秒 1000 - 7511 0.00 0.00 0.00 0.00 0.00 0 |__java
11时36分57秒 1000 - 7512 0.00 0.00 0.00 0.00 0.00 0 |__java
11时36分57秒 1000 - 7513 0.00 0.00 0.00 0.00 0.00 0 |__java
11时36分57秒 1000 - 7514 0.00 0.00 0.00 0.00 0.00 2 |__java
11时36分57秒 1000 - 7515 0.00 0.00 0.00 0.00 0.00 3 |__java
11时36分57秒 1000 - 7516 0.00 0.00 0.00 0.00 0.00 2 |__java
11时36分57秒 1000 - 7517 0.00 0.00 0.00 0.00 0.00 2 |__java
11时36分57秒 1000 - 7518 0.00 0.00 0.00 0.00 0.01 2 |__java
11时36分57秒 1000 - 7566 0.00 0.00 0.00 0.00 0.00 2 |__java
top结果
# top -H -p ${pid}
top - 11:34:24 up 9 min, 1 user, load average: 1.83, 2.11, 1.20
Threads: 16 total, 1 running, 15 sleeping, 0 stopped, 0 zombie
%Cpu(s): 9.4 us, 1.9 sy, 0.0 ni, 65.8 id, 22.8 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem : 16129972 total, 11827668 free, 1883300 used, 2419004 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 13757492 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7503 qudongf+ 20 0 6554144 25896 17048 R 99.9 0.2 1:00.37 java
7501 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7506 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7507 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7508 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7509 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7510 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7511 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7512 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7513 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7514 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7515 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7516 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7517 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7518 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
7566 qudongf+ 20 0 6554144 25896 17048 S 0.0 0.2 0:00.00 java
对jstack结果解释
线程名 | daemon | native | 用途 | 个数 | 备注 |
---|---|---|---|---|---|
main | N | N | 主线程 | 1 | |
C1 CompilerThread* | Y | Y | C1编译(解释器) | 1 | 平台相关且与启动参数有关 |
C2 CompilerThread* | Y | Y | C2编译(JIT Compiler) | 2 | 平台相关且与启动参数有关 |
GC task thread#* | Y | Y | GC | 4 | 平台相关且与启动参数有关 |
Finalizer | Y | N | Finalizer处理 | 1 | * |
Reference Handler | Y | N | 引用处理 | 1 | * |
VM Thread | Y | Y | 执行VM级别(需要safe-point的)操作,如响应STW的场景(如fullgc,stackdump) | 1 | jvm第一个线程 |
VM Periodic Task Thread | Y | WatcherThread的单例,模拟时钟中断 | 1 | * | |
Signal Dispatcher | Y | Y | 处理系统信号并分发给java信号处理函数 | 1 | * |
Attach Listener | Y | Y | client请求服务线程,处理jdk支持的AttachOperation | 1 | 支持延迟加载,取决于平台和启动参数 |
Service Thread | Y | Y | 低内存探测/JVMTI事件转发(非JavaThread不能直接发) | 1 | java7及以下名称“Low Memory Detector” |
共计 | * | * | * | 15 |
总结:hello world程序启动的线程分以下几类:
- 用户线程,如main
- GC相关线程,包括C1/C2;与运行机器配置、操作系统、启动参数相关
- 编译器/解释器相关线程;与运行机器配置、操作系统、启动参数相关
- 引用及Finalizer处理线程
- JVM其他后台线程
- VM Thread:可以说它类似linux的init进程
- VM Periodic Task Thread:JVM时钟模拟线程
- Signal Dispatcher:JVM信号处理线程
- Attach Listener:jdk工具类请求响应处理线程
- Service Thread:低内存检测、JVMTI事件转发线程
Ref
- http://man7.org/linux/man-pages/man1/pstree.1.html
- http://man7.org/linux/man-pages/man1/pidstat.1.html
- http://man7.org/linux/man-pages/man1/top.1.html
- http://blog.jamesdbloom.com/JVMInternals.html
- http://jakubstransky.com/2017/12/19/hotspot-jvm-internal-threads/
- http://openjdk.java.net/groups/hotspot/docs/RuntimeOverview.html#Thread%20Management%7Coutline
- http://hg.openjdk.java.net/jdk8u/jdk8u-dev/hotspot/file/d3cc20285653/src/share/vm/runtime/os.hpp#l439
- http://www.oracle.com/technetwork/java/jdk50-ts-guide-149808.pdf
- http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/cadea780bc76/src/share/classes/java/lang/ref/Finalizer.java
- http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/cadea780bc76/src/share/classes/java/lang/ref/Reference.java
- http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/1c0a59cee0e4/src/share/vm/runtime/vmThread.cpp
- http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/1c0a59cee0e4/src/share/vm/runtime/os.cpp#l250
- http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/1c0a59cee0e4/src/share/vm/services/attachListener.hpp#l33
- http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/1c0a59cee0e4/src/os/linux/vm/attachListener_linux.cpp#l42
- http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/1c0a59cee0e4/src/share/vm/runtime/serviceThread.hpp#l30
- http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/1c0a59cee0e4/src/share/vm/runtime/serviceThread.cpp#l44