Threadのメモリモデルを色々調べていくうちにJavaのスレッドでOSスレッドとどうやって紐付いてるんだろうと思って調べた。
JavaにおけるTheradスケジューラはOSに依存するみたいな説明がされているが、要はJVMプロセス内でOSスレッドを生成してそれをwrapしている。
知識としては知っていたけど、改めて確認してみたかったメモ
public class Main { public static void main(String args[]) throws Exception { Thread.sleep(1000 * 60 * 60); // プロセス終了しないように止めておく } }
javacしてjava Mainでjavaプロセス起動してからPID確認
root@dc4d74fd6f08:/# jps 5684 Jps 5498 Main ←ここにいる
まずはjstackでjava threadを確認
root@dc4d74fd6f08:/# jstack 5498 | grep nid "Attach Listener" #9 daemon prio=9 os_prio=0 tid=0x00007f29a0001000 nid=0x15ba waiting on condition [0x0000000000000000] "Service Thread" #8 daemon prio=9 os_prio=0 tid=0x00007f29dc0c0000 nid=0x1587 runnable [0x0000000000000000] "C1 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f29dc0b0800 nid=0x1586 waiting on condition [0x0000000000000000] "C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f29dc0af000 nid=0x1585 waiting on condition [0x0000000000000000] "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f29dc0ac000 nid=0x1584 waiting on condition [0x0000000000000000] "Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f29dc0aa000 nid=0x1583 runnable [0x0000000000000000] "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f29dc083800 nid=0x1582 in Object.wait() [0x00007f29c5a50000] "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f29dc07f000 nid=0x1581 in Object.wait() [0x00007f29c5b51000] "main" #1 prio=5 os_prio=0 tid=0x00007f29dc009800 nid=0x157b waiting on condition [0x00007f29e36ea000] "VM Thread" os_prio=0 tid=0x00007f29dc077000 nid=0x1580 runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f29dc01f000 nid=0x157c runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f29dc020800 nid=0x157d runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f29dc022000 nid=0x157e runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f29dc024000 nid=0x157f runnable "VM Periodic Task Thread" os_prio=0 tid=0x00007f29dc0c2800 nid=0x1588 waiting on condition root@dc4d74fd6f08:/# jstack 5498 | grep nid | wc -l 15
jstack的には15個のスレッドがあった
次にpsコマンドでOSスレッドを確認
root@dc4d74fd6f08:/# ps -feL | grep "java Main" UID PID PPID LWP C NLWP STIME TTY TIME CMD root 5498 1 5498 0 16 11:36 ? 00:00:00 java Main root 5498 1 5499 0 16 11:36 ? 00:00:00 java Main root 5498 1 5500 0 16 11:36 ? 00:00:00 java Main root 5498 1 5501 0 16 11:36 ? 00:00:00 java Main root 5498 1 5502 0 16 11:36 ? 00:00:00 java Main root 5498 1 5503 0 16 11:36 ? 00:00:00 java Main root 5498 1 5504 0 16 11:36 ? 00:00:00 java Main root 5498 1 5505 0 16 11:36 ? 00:00:00 java Main root 5498 1 5506 0 16 11:36 ? 00:00:00 java Main root 5498 1 5507 0 16 11:36 ? 00:00:00 java Main root 5498 1 5508 0 16 11:36 ? 00:00:00 java Main root 5498 1 5509 0 16 11:36 ? 00:00:00 java Main root 5498 1 5510 0 16 11:36 ? 00:00:00 java Main root 5498 1 5511 0 16 11:36 ? 00:00:00 java Main root 5498 1 5512 0 16 11:36 ? 00:00:00 java Main root 5498 1 5562 0 16 11:38 ? 00:00:00 java Main root@dc4d74fd6f08:/# ps -feL | grep "java Main"| wc -l 16
ps的には16個あった
psコマンド的にはjstack比べてスレッドが一個多いのは
>root 5498 1 5498 0 16 11:36 ? 00:00:00 java Main
これが大元のjava親プロセスでメインスレッドを作るときにforkして子プロセスを作ってるっぽい(PIDとLWPが同じなので)
- 1Processは1CPUで実行される
- なので概念的にはProcessの中にいくらThreadを作成して並行化しようが、1CPUしか使わない
- そこでLinuxとしてはLWP(Light Weight Process)としてメモリ共有したプロセス(軽量プロセス)を生成し、Threadとして扱う
- 昔のLinuxはLWPがなかったので、単なるThreadだったのでマルチコアで多重スレッドにはできなかった
- 実際、上記のps -feLの結果確認するとLWPのIDが全部違うのでOSスレッド = 軽量プロセスということになっている
スッキリした