通过jstack工具可以查看JVM中所有的线程,其中有一些比较特殊的系统线程,下面主要讲讲这些系统线程的作用。
1.Attach Listener线程
Attach Listener线程是负责接收外部的命令,对该命令进行执行,并把结果返回给发送者。通常我们会去用一些命令与JVM进行交互,比如java -version、jmap、jstack等。如果该线程在JVM启动的时候没有初始化,那么用户在第一次执行JVM命令时,该线程会被启动。
堆栈信息如下所示:
"Attach Listener" #173 daemon prio=9 os_prio=0 cpu=0.31ms elapsed=0.20s tid=0x00007f9ea0001000 nid=0x5edf waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
该线程为daemon线程,大部分时间处于waiting on condition状态。
2.DestroyJavaVM线程
JVM的main线程结束后,会调用JNI中的jni_destoryjavavm方法唤起DestroyJavaVM线程。
DestroyJavaVM的线程逻辑定义在threads.cpp中的destroy_vm方法中,如下所示:
void Threads::destroy_vm() {
JavaThread* thread = JavaThread::current();
#ifdef ASSERT
_vm_complete = false;
#endif
// Wait until we are the last non-daemon thread to execute, or
// if we are a daemon then wait until the last non-daemon thread has
// executed.
bool daemon = java_lang_Thread::is_daemon(thread->threadObj());
int expected = daemon ? 0 : 1;
{
MonitorLocker nu(Threads_lock);
while (Threads::number_of_non_daemon_threads() > expected)
// This wait should make safepoint checks, wait without a timeout.
nu.wait(0);
}
EventShutdown e;
if (e.should_commit()) {
e.set_reason("No remaining non-daemon Java threads");
e.commit();
}
// Hang forever on exit if we are reporting an error.
if (ShowMessageBoxOnError && VMError::is_error_reported()) {
os::infinite_sleep();
}
os::wait_for_keypress_at_exit();
// run Java level shutdown hooks
thread->invoke_shutdown_hooks();
before_exit(thread);
thread->exit(true);
……
}
JVM在Jboss服务器启动之后就会唤起DestroyJavaVM线程,之后处于等待状态,等待其他线程(java线程和native线程)退出时通知他卸载JVM。线程退出时会判断自己是否是整个JVM中最后一个非daemon线程,如果是最后一个非daemon线程,则通知DestroyJavaVM线程卸载JVM。
如果线程退出时,判断自己不是最后一个非daemon线程,那么调用thread.exit(false),并抛出thread_end事件,JVM不退出。
如果线程退出时,判断自己是最后一个非daemon线程,那么先调用invoke_shutdown_hooks方法触发所有Java级别的shutdown钩子函数的执行(可参考《JVM shutdown hook》文章内容),然后调用before_exit()方法,抛出两个事件:
(1)thread_end线程结束事件
(2)VM的death事件,然后调用thread.exit(true)方法,接下来把线程从active_list卸下、删除线程等一系列工作完成后,通知正在等待的DestroyJavaVM线程执行卸载JVM操作。
堆栈信息如下所示:
"DestroyJavaVM" #170 prio=5 os_prio=0 cpu=17871.39ms elapsed=34.59s tid=0x00007f9f1c016000 nid=0x4ba0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
该线程不是daemon线程。
3.Signal Dispatcher线程
Attach Listener线程负责接收外部JVM命令,当命令接收成功后,会把指令交给Signal Dispatcher线程分发到不同的模块进行处理,并且返回结果。该线程也是在第一次接收外部JVM命令时进行初始化的。
堆栈信息如下所示:
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 cpu=0.50ms elapsed=84.44s tid=0x00007f9f1c091000 nid=0x4bb6 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
该线程是daemon线程。
4.Finalizer线程
这个线程也是main线程之后创建的,优先级为8,主要用于垃圾收集前,调用对象的finalize方法。
(1)只有当一轮垃圾收集时,才会开始调用finalize方法,并不是所有对象的finalize方法都会被执行。
(2)该线程为daemon线程,如果虚拟机中没有其他非daemon线程,不管该线程是否执行完finalize方法,JVM也会退出。
(3)JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理。最后将该Finalizer对象的引用置为null,由垃圾收集器来回收。
(4)JVM为什么要单独用一个线程来执行finalize方法呢?如果JVM的垃圾收集线程自己来做,很有可能由于该方法中的误操作导致GC线程停止或不可控,这对GC线程来说事一种灾难。
堆栈信息如下所示:
"Finalizer" #3 daemon prio=8 os_prio=0 cpu=33.64ms elapsed=84.50s tid=0x00007f9f1c083800 nid=0x4bb0 in Object.wait() [0x00007f9eee786000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000070000dab8> (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)
5.Reference Handler线程
JVM在创建main线程后就创建Reference Handler线程,其优先级最高,为10,主要用于引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。
堆栈信息如下所示:
"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=23.54ms elapsed=84.50s tid=0x00007f9f1c077800 nid=0x4baf in Object.wait() [0x00007f9eeeaa7000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000070000df80> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)