JVM_main方法分析

  1. java线程
    1. Reference Handler #2
    2. Finalizer #3
    3. Signal Dispatcher #4
    4. Monitor Ctrl-Break #5
    5. Attach Listener
  2. ThreadMXBean查看
  3. jstack查看
    1. Jstack 文件
    2. vm内部线程(VM internal JavaThreads)

纵然伤心 也不要愁眉不展 因为你不知道谁会爱上你的笑容 ——飞鸟集

启动一个最简单的Java main程序时,有多少个线程被创建? 有什么方法可以看到当前运行的线程?

  • java线程
  • 如何查看线程状态
  • 线程类型

java线程

在java中,启动一个简单的main程序,并不是只是单单创建了一个main线程而已,JVM会自动创建一些辅助用的线程,主要有以下几个:

Reference Handler #2

VM在创建main线程后就创建Reference Handler线程,其优先级最高,为10,它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。

Finalizer #3

这个线程也是在main线程之后创建的,其优先级为10,主要用于在垃圾收集前,调用对象的finalize()方法;关于Finalizer线程的几点:

  1)只有当开始一轮垃圾收集时,才会开始调用finalize()方法;因此并不是所有对象的finalize()方法都会被执行;

  2)该线程也是daemon线程,因此如果虚拟机中没有其他非daemon线程,不管该线程有没有执行完finalize()方法,JVM也会退出;

  3) JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理;最后将该Finalizer对象的引用置为null,由垃圾收集器来回收;

  4) JVM为什么要单独用一个线程来执行finalize()方法呢?如果JVM的垃圾收集线程自己来做,很有可能由于在finalize()方法中误操作导致GC线程停止或不可控,这对GC线程来说是一种灾难;

Signal Dispatcher #4

前面我们提到第一个Attach Listener线程的职责是接收外部jvm命令,当命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather线程也是在第一次接收外部jvm命令时,进行初始化工作。

Monitor Ctrl-Break #5

Monitoring Thread Activity With Thread Dumps Thread dumps, or “thread stack traces,” reveal information about an application’s activity that can help you diagnose problems and better optimize application and JVM performance; for example, thread dumps can show the occurrence of “deadlock” conditions, which can seriously impact application performance. You can create a thread dump by invoking a control break (usually by pressing Ctrl-Break or Ctrl-\ or SIGQUIT on linux). This section provides information on working with thread dumps. It includes information on these subjects: 1.Lock Information in Thread Dumps 2.Detecting Deadlocks

Attach Listener

Attach Listener线程是负责接收到外部的命令,而对该命令进行执行的并且吧结果返回给发送者。通常我们会用一些命令去要求jvm给我们一些反 馈信 息,如:java -version、jmap、jstack等等。如果该线程在jvm启动的时候没有初始化,那么,则会在用户第一次执行jvm命令时,得到启动。

以上是被创建的java线程

ThreadMXBean查看

如下代码

 public class MainThreadInfoDemo {
    public static void main(String[] args) {
//        threadCreateExample();
        dumpThreadInfo();

    }

    public static void dumpThreadInfo() {
        Arrays.stream(ManagementFactory.getThreadMXBean().dumpAllThreads(false, false)).
                forEach(threadInfo -> System.out.println("[" + threadInfo.getThreadId() + "]" + threadInfo.getThreadName())
                );
    }

    /**
     * 线程创建example
     */
    public static void threadCreateExample() {
        class DemoThread extends Thread {
            @Override
            public void run() {
                System.out.println("extend");
                while (true) {
                }
            }
        }
        new DemoThread().start();

        new Thread(() -> { System.out.println("impl");while (true) { } }).start();
    }
}

T

运行结果如下:

[5]Monitor Ctrl-Break
[4]Signal Dispatcher
[3]Finalizer
[2]Reference Handler
[1]main

把threadCreateExample注释去掉,运行方法后发现:

[12]Thread-1
[11]Thread-0
[5]Monitor Ctrl-Break
[4]Signal Dispatcher
[3]Finalizer
[2]Reference Handler
[1]main

接下来我们来看看为什么id是从11开始 ?

jstack查看

Jstack 文件

用jstack 查看(没有运行threadCreateExample方法)

2019-08-29 11:44:28
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode):

"Attach Listener" #11 daemon prio=9 os_prio=31 tid=0x00007fcf2582d000 nid=0x5603 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Service Thread" #10 daemon prio=9 os_prio=31 tid=0x00007fcf25093800 nid=0x3d03 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C1 CompilerThread3" #9 daemon prio=9 os_prio=31 tid=0x00007fcf25802800 nid=0x3b03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C2 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007fcf24874000 nid=0x3f03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007fcf24025000 nid=0x4103 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007fcf25801800 nid=0x3803 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007fcf229b1000 nid=0x3703 runnable [0x000070000feff000]
   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 <0x000000076af13bc8> (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 <0x000000076af13bc8> (a java.io.InputStreamReader)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

   Locked ownable synchronizers:
    - None

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

   Locked ownable synchronizers:
    - None

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fcf2280c800 nid=0x4a03 in Object.wait() [0x000070000fcf9000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076ab08ec0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    - locked <0x000000076ab08ec0> (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)

   Locked ownable synchronizers:
    - None

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

   Locked ownable synchronizers:
    - None

"main" #1 prio=5 os_prio=31 tid=0x00007fcf25004800 nid=0x1803 waiting on condition [0x000070000f1d8000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000076ae01da0> (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 lang.ThreadDemo.main(ThreadDemo.java:27)

   Locked ownable synchronizers:
    - None

"VM Thread" os_prio=31 tid=0x00007fcf25015000 nid=0x4d03 runnable 

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fcf26000800 nid=0x1f07 runnable 

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fcf24000800 nid=0x2a03 runnable 

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fcf22000800 nid=0x5303 runnable 

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fcf25010000 nid=0x2b03 runnable 

"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007fcf22014000 nid=0x5103 runnable 

"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007fcf24001000 nid=0x5003 runnable 

"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007fcf22014800 nid=0x2e03 runnable 

"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007fcf22015000 nid=0x4e03 runnable 

"VM Periodic Task Thread" os_prio=31 tid=0x00007fcf250c5000 nid=0x5503 waiting on condition 

JNI global references: 33

可以发现 其中 6-10为:

  • “C2 CompilerThread0” #6
  • “C2 CompilerThread1” #7
  • “C2 CompilerThread2” #8
  • “C1 CompilerThread3” #9
  • “Service Thread” #10

为什么这些在dumpThreadInfo 没有打印出来?

vm内部线程(VM internal JavaThreads)

在/src/share/vm/prims/jvm.cpp中

// Returns an array of all live Thread objects (VM internal JavaThreads,
// jvmti agent threads, and JNI attaching threads  are skipped)
// See CR 6404306 regarding JNI attaching threads
JVM_ENTRY(jobjectArray, JVM_GetAllThreads(JNIEnv *env, jclass dummy))
  ResourceMark rm(THREAD);
  ThreadsListEnumerator tle(THREAD, false, false);
  JvmtiVMObjectAllocEventCollector oam;

  int num_threads = tle.num_threads();
  objArrayOop r = oopFactory::new_objArray(SystemDictionary::Thread_klass(), num_threads, CHECK_NULL);
  objArrayHandle threads_ah(THREAD, r);

  for (int i = 0; i < num_threads; i++) {
    Handle h = tle.get_threadObj(i);
    threads_ah->obj_at_put(i, h());
  }

其中简单翻译一下方法的注释
返回所有活动线程对象的数组(跳过VM内部JavaThreads、jvmti代理线程和JNI附加线程),请参阅CR 6404306关于JNI附加线程的信息

可以知道不会打印VM内部的JavaThreads

另外的 JVM自身的线程(初始化的时候创建的,非java线程 没有prio=5)

  • VM Thread
  • GC task thread#0-7 (8核)
  • VM Periodic Task Thread

Thread.getAllStackTraces 其实也是 private static native Thread[] getThreads();

http://ifeve.com/jvm-thread/,可以取这个地址查看更多线程的信息


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 951488791@qq.com

文章标题:JVM_main方法分析

字数:1.9k

本文作者:zhengyumin

发布时间:2019-08-29, 23:38:13

最后更新:2020-01-12, 23:12:26

原始链接:http://zyumin.github.io/2019/08/29/JVM-main%E6%96%B9%E6%B3%95%E5%88%86%E6%9E%90/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。