Linux 운영 체제 관점에서 Java/JVM은 어떤 유형의 스레드를 사용합니까?

Linux 운영 체제 관점에서 Java/JVM은 어떤 유형의 스레드를 사용합니까?

내 개발자 친구가 최근에 다음과 같은 질문을 했습니다. Linux 시스템에서 스레드가 있는 Java 애플리케이션을 실행할 때 해당 스레드가 기본 Linux 운영 체제에 어떻게 표시됩니까?

그렇다면 Java 스레드란 무엇입니까?

답변1

총 길이 DR

존재하다자바 1.1, 녹색 스레드가 사용되는 유일한 스레딩 모델입니다.자바 가상 머신(JVM),9적어도 안으로솔라리스. 녹색 스레드는 기본 스레드에 비해 몇 가지 제한 사항이 있기 때문에 Java의 후속 버전에서는 기본 스레드를 선호하여 녹색 스레드를 삭제했습니다.10,11.

원천:녹색 실

다음 그림은 운영 체제 관점에서 Java 스레드를 분석하는 방법을 보여줍니다.


배경

이 문제를 조사하는 동안 다음과 같은 제목의 질문과 답변을 발견했습니다.Java JVM은 pthread를 사용합니까? . 이 질문에는 JVM 소스 코드에 대한 링크가 있습니다.OpenJDK/jdk8u/jdk8u/핫스팟. 특히 이 단락은 다음과 같습니다.

    // Serialize thread creation if we are running with fixed stack LinuxThreads
    bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
    if (lock) {
      os::Linux::createThread_lock()->lock_without_safepoint_check();
    }

    pthread_t tid;
    int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

    pthread_attr_destroy(&attr);

여기서 우리는 JVM이 일명 pthread를 사용하고 있음을 볼 수 있습니다.POSIX 스레드. 추가 세부정보pthreads(7) 매뉴얼 페이지:

POSIX.1은 종종 POSIX 스레드 또는 Pthread라고 불리는 스레드 프로그래밍을 위한 인터페이스(함수, 헤더 파일) 세트를 지정합니다. 단일 프로세스에는 여러 스레드가 포함될 수 있으며 모두 동일한 프로그램을 실행합니다. 이러한 스레드는 동일한 전역 메모리(데이터 및 힙 세그먼트)를 공유하지만 각 스레드에는 자체 스택(자동 변수)이 있습니다.

이러한 이유로 Java에 존재하는 유일한 스레드는 Linux의 pthread입니다.

실험

이를 더 자세히 증명하기 위해 다음 예를 사용할 수 있습니다.스칼라 앱결국 이것은 단지 Java 애플리케이션일 뿐입니다.

애플리케이션은 Docker 컨테이너에서 실행되지만 이를 사용하여 스레드를 사용하는 실행 중인 Java 애플리케이션을 연구할 수 있습니다. 이 애플리케이션을 사용하려면 Git 저장소를 복제한 다음 Docker 컨테이너를 빌드하고 실행하면 됩니다.

애플리케이션 빌드
$ git clone https://github.com/slmingol/jvmthreads.git
$ cd jvmthreads
$ docker build -t threading .
$  docker run -it -v ~/.coursier/cache:/root/.cache/coursier -v ~/.ivy2:/root/.ivy2  -v ~/.sbt:/root/.sbt -v ~/.bintray:/root/.bintray -v $(pwd):/threading threading:latest /bin/bash

이 시점에서 Docker 컨테이너 내부에 있어야 하며 다음 유형의 프롬프트가 나타나야 합니다.

root@27c0fa503da6:/threading#
애플리케이션 실행

여기에서 애플리케이션을 실행하려고 합니다 sbt.

$ sbt compile compileCpp "runMain com.threading.ThreadingApp"

Ctrl이 애플리케이션이 실행되기 시작하면 애플리케이션에서 +를 사용하여 연구할 수 있습니다.ZSIGSTP

root@27c0fa503da6:/threading#  sbt compile compileCpp "runMain com.threading.ThreadingApp"
[info] Loading settings from metaplugins.sbt ...
[info] Loading project definition from /threading/project/project
[info] Loading settings from plugins.sbt ...
[info] Loading project definition from /threading/project
[info] Loading settings from build.sbt ...
[warn] Missing bintray credentials. Either create a credentials file with the bintrayChangeCredentials task, set the BINTRAY_USER and BINTRAY_PASS environment variables or pass bintray.user and bintray.pass properties to sbt.
[warn] Missing bintray credentials. Either create a credentials file with the bintrayChangeCredentials task, set the BINTRAY_USER and BINTRAY_PASS environment variables or pass bintray.user and bintray.pass properties to sbt.
^Z
[1]+  Stopped                 sbt compile compileCpp "runMain com.threading.ThreadingApp"
분석 애플리케이션

ps이제 여기에서 테스트 중인 애플리케이션이 운영 체제 관점에서 어떻게 작동하는지 확인하는 등 일반적인 UNIX 도구를 사용할 수 있습니다 .

표준 pscmd에는 실행 중인 일반적인 응용 프로그램만 표시됩니다.

root@27c0fa503da6:/threading# ps -eaf
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 02:14 pts/0    00:00:00 /bin/bash
root      1503     1  0 02:37 pts/0    00:00:00 bash /usr/bin/sbt compile compileCpp runMain com.threading.ThreadingApp
root      1571  1503 98 02:37 pts/0    00:00:35 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar
root      1707  1571  0 02:37 pts/0    00:00:00 git describe --tags --abbrev=8 --match v[0-9]* --always --dirty=+20191026-0237
root      1718     1  0 02:37 pts/0    00:00:00 ps -eaf

그러나 ps스레드 보기를 사용하면 더 완전한 그림을 볼 수 있습니다.

root@27c0fa503da6:/threading# ps -eLf | head -8
UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
root         1     0     1  0    1 02:14 pts/0    00:00:00 /bin/bash
root      1943     1  1943  0    1 03:08 pts/0    00:00:00 bash /usr/bin/sbt compile compileCpp runMain com.threading.ThreadingApp
root      2011  1943  2011  0   32 03:08 pts/0    00:00:00 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
root      2011  1943  2012  0   32 03:08 pts/0    00:00:05 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
root      2011  1943  2013  0   32 03:08 pts/0    00:00:00 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
root      2011  1943  2014  0   32 03:08 pts/0    00:00:00 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp
root      2011  1943  2015  0   32 03:08 pts/0    00:00:00 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar compile compileCpp runMain com.threading.ThreadingApp

노트:위에서 우리는 많은 스레드가 있음을 볼 수 있습니다. 이 보기에서 관심 있는 열은 LWP및 입니다 NLWP.

  • LWP경량 프로세스 스레드를 나타냅니다.
  • NLWPLWP 수량을 나타냅니다.

NLWP 숫자는 PID와 관련된 총 스레드 수를 알려주기 때문에 중요합니다. 이 예에서 해당 숫자는 32입니다. 다음과 같이 확인할 수 있습니다.

root@27c0fa503da6:/threading# ps -eLf|grep -E "[3]2.*java" | wc -l
32

ps다음 명령을 사용하여 이러한 스레드를 확인하는 다른 방법을 얻을 수도 있습니다.

root@27c0fa503da6:/threading# ps -Lo pid,lwp,pri,nice,start,stat,bsdtime,cmd,comm | head -5
  PID   LWP PRI  NI  STARTED STAT   TIME CMD                         COMMAND
    1     1  19   0 02:14:42 Ss     0:00 /bin/bash                   bash
 1943  1943  19   0 03:08:41 T      0:00 bash /usr/bin/sbt compile c bash
 2011  2011  19   0 03:08:41 Tl     0:00 java -Xms1024m -Xmx1024m -X java
 2011  2012  19   0 03:08:41 Tl     0:05 java -Xms1024m -Xmx1024m -X java

참고 1:lSTAT 열로 인해 이 형식은 이것이 pthread임을 보여줍니다.

  • S- 중단 가능한 절전 모드(이벤트가 완료될 때까지 대기)
  • T- 작업 제어 신호에 의해 중지됨
  • l- 다중 스레드입니다(NPTL pthread와 마찬가지로 CLONE_THREAD 사용).

노트 2:SIGSTP 제어 신호 +를 사용하여 프로세스가 중지 되었음을 나타내기 S때문에 여기서 중요합니다 .TCtrlZ

ps -T스위치를 사용하여 스레드로 처리 할 수도 있습니다 .

root@27c0fa503da6:/threading# ps -To pid,tid,tgid,tty,time,comm | head -5
  PID   TID  TGID TT           TIME COMMAND
    1     1     1 pts/0    00:00:00 bash
 1943  1943  1943 pts/0    00:00:00 bash
 2011  2011  2011 pts/0    00:00:00 java
 2011  2012  2011 pts/0    00:00:05 java

위의 스위치 ps:

   -L     Show threads, possibly with LWP and NLWP columns.
   -T     Show threads, possibly with SPID column.

앱을 완전히 실행하세요.

참조 목적으로 궁금하신 경우 Scala/Java 애플리케이션의 전체 실행을 확인하세요.

root@27c0fa503da6:/threading# sbt compile compileCpp "runMain com.threading.ThreadingApp"
[info] Loading settings from build.sbt ...
[warn] Missing bintray credentials. Either create a credentials file with the bintrayChangeCredentials task, set the BINTRAY_USER and BINTRAY_PASS environment variables or pass bintray.user and bintray.pass properties to sbt.
[warn] Missing bintray credentials. Either create a credentials file with the bintrayChangeCredentials task, set the BINTRAY_USER and BINTRAY_PASS environment variables or pass bintray.user and bintray.pass properties to sbt.
[info] Set current project to threading (in build file:/threading/)
[info] Executing in batch mode. For better performance use sbt's shell
[warn] Credentials file /root/.bintray/.credentials does not exist, ignoring it
[success] Total time: 2 s, completed Oct 26, 2019 4:11:38 AM
[success] Total time: 1 s, completed Oct 26, 2019 4:11:39 AM
[warn] Credentials file /root/.bintray/.credentials does not exist, ignoring it
[info] Running (fork) com.threading.ThreadingApp
[info] Started a linux thread 140709608359680!
[info] Started a linux thread 140709599966976!
[info] Starting  thread_entry_pointStarted a linux thread 140709591574272!
[info] Starting  thread_entry_pointStarting  thread_entry_pointStarted a linux thread 140709583181568!
[info] Running Thread 1
[info] Starting  thread_entry_pointStarted a linux thread 140709369739008!
[info] Running Thread 2
[info] Starting  thread_entry_pointStarted a linux thread 140709608359680!
[info] Running Thread 3
[info] Starting  thread_entry_pointStarted a linux thread 140709599966976!
[info] Running Thread 4
[info] Running Thread 5Starting  thread_entry_pointStarting  thread_entry_pointStarted a linux thread 140709361346304!
[info] Running Thread 6
[info] Starting  thread_entry_pointStarted a linux thread 140709583181568!
[info] Started a linux thread 140709591574272!
[info] Starting  thread_entry_pointStarting  thread_entry_pointStarted a linux thread 140709352953600!
[info] Running Thread 7
[info] Running Thread 9
[info] Started a linux thread 140709369739008!
[info] Starting  thread_entry_pointStarted a linux thread 140709608359680!
[info] Running Thread 8
[info] Starting  thread_entry_pointStarted a linux thread 140709344560896!
[info] Starting  thread_entry_pointStarted a linux thread 140709583181568!
[info] Starting  thread_entry_pointStarted a linux thread 140709599966976!
[info] Starting  thread_entry_pointStarted a linux thread 140709336168192!
[info] Running Thread 10
[info] Running Thread 11
[info] Starting  thread_entry_pointStarted a linux thread 140709327775488!
[info] Running Thread 12Started a linux thread 140709591574272!
[info] Running Thread 13
[info] Running Thread 14
[info] Running Thread 16
[info] Running Thread 15
[info] Running Thread 18
[info] Running Thread 17
[info] Running Thread 19
[info] Starting  thread_entry_pointStarting  thread_entry_point
[success] Total time: 1 s, completed Oct 26, 2019 4:11:40 AM

스레드 덤프?

어떤 사람들은 Java 스레드를 Linux LWP와 어떻게 연관시키는지 묻습니다. 이를 위해 Java 스레드 덤프를 사용하여 2를 비교할 수 있습니다.

Ctrl다시 한 번 위와 동일한 Scala 애플리케이션을 사용하고 + 로 이동합니다 Z.

root@52a4b6e78711:/threading# sbt compile compileCpp "runMain com.threading.ThreadingApp"
[info] Loading settings from metaplugins.sbt ...
[info] Loading project definition from /threading/project/project
^Z
[1]+  Stopped                 sbt compile compileCpp "runMain com.threading.ThreadingApp"

이 작업이 완료되면 SIGQUIT를 JVM에 보내야 합니다. 이렇게 하려면 일반적으로 다음을 사용할 수 있습니다 kill -3 <PID of JVM>.

root@52a4b6e78711:/threading# ps -eaf
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 12:36 pts/0    00:00:00 /bin/bash
root         7     1  0 12:37 pts/0    00:00:00 bash /usr/bin/sbt compile compileCpp runMain com.threading.ThreadingApp
root        75     7 99 12:37 pts/0    00:00:17 java -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=128m -XX:MaxMetaspaceSize=256m -jar /usr/share/sbt/bin/sbt-launch.jar
root       130     1  0 12:37 pts/0    00:00:00 ps -eaf

root@52a4b6e78711:/threading# kill -3 75

그런 다음 프로그램이 다시 시작되도록 허용해야 합니다 fg.

root@52a4b6e78711:/threading# fg
sbt compile compileCpp "runMain com.threading.ThreadingApp"
2019-10-26 12:38:00
Full thread dump OpenJDK 64-Bit Server VM (25.181-b13 mixed mode):

"scala-execution-context-global-32" #32 daemon prio=5 os_prio=0 tid=0x00007f87d8002800 nid=0x80 runnable [0x00007f880973d000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000c1b08a68> (a scala.concurrent.impl.ExecutionContextImpl$$anon$3)
    at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

"scala-execution-context-global-33" #33 daemon prio=5 os_prio=0 tid=0x00007f87dc001000 nid=0x7f runnable [0x00007f880983e000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000c1b08a68> (a scala.concurrent.impl.ExecutionContextImpl$$anon$3)
    at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

"scala-execution-context-global-31" #31 daemon prio=5 os_prio=0 tid=0x00007f87d8001000 nid=0x7e waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"scala-execution-context-global-30" #30 daemon prio=5 os_prio=0 tid=0x00007f87e4003800 nid=0x7d waiting on condition [0x00007f8809a40000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000c1b08a68> (a scala.concurrent.impl.ExecutionContextImpl$$anon$3)
    at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

노트:위 명령의 부분 출력에서 kill -3​​JVM의 스레드가 분석 없이 정렬되어 있는 것을 볼 수 있습니다. 그 중 32개가 있는데, 이는 Java 스레드가 실제로 Linux LWP와 1:1임을 보여줍니다.

인용하다

관련 정보