我们通常会使用工具 jstack 去跟踪线程信息, 其如何实现使用 attach 的方式还是 ptrace 的方式, 这些可以去参考本人的博客的其他文章.
但这些方式都是外部使用的方式, 如何直接使用 java 代码得到当前进程的线程的信息, 方便监控 jvm 的整个运行状态, 就不的不提到了 ManagementFactory
通过调用方法
ThreadMXBean tmbean = ManagementFactory.getThreadMXBean();
通过得到 ThreadMXBean 可以得到非常多的 thread 信息, 博客里也主要提到几个重要函数的实现
ThreadMXBean 是个接口, 主要实现都是在 ThreadImpl.java 里实现.
1. getThreadCount()
在虚拟机里会有 ThreadService, 里面会有些计数器用于记录总线程数, 活着线程数目
- PerfCounter* ThreadService::_total_threads_count = NULL;
- PerfVariable* ThreadService::_live_threads_count = NULL;
- PerfVariable* ThreadService::_peak_threads_count = NULL;
- PerfVariable* ThreadService::_daemon_threads_count = NULL;
- volatile int ThreadService::_exiting_threads_count = 0;
- volatile int ThreadService::_exiting_daemon_threads_count = 0;
当线程创建, 消亡都会调用 ThreadService 的方法来对计数器加减, 这样就能直接得到线程数目的状态, 而不需要去遍历线程链表.
2. getAllThreadIds()
这个实现也比较简单, 直接扫描线程列表, 就可以得到每个 java 的线程 id, 在扫描过程中使用了锁, 锁住了线程链表. 但因为从 native 代码到 java 代码中没有锁结构, 得到线程的列表只能表示当时的状态, 当得到 id 的时候并不能保证该线程依然存活.
- ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread,
- bool include_jvmti_agent_threads,
- bool include_jni_attaching_threads) {
- assert(cur_thread == Thread::current(), "Check current thread");
- int init_size = ThreadService::get_live_thread_count();
- _threads_array = new GrowableArray<instanceHandle>(init_size);
- MutexLockerEx ml(Threads_lock);
- for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
- // skips JavaThreads in the process of exiting
- // and also skips VM internal JavaThreads
- // Threads in _thread_new or _thread_new_trans state are included.
- // i.e. threads have been started but not yet running.
- if (jt->threadObj() == NULL ||
- jt->is_exiting() ||
- !java_lang_Thread::is_alive(jt->threadObj()) ||
- jt->is_hidden_from_external_view()) {
- continue;
- }
- // skip agent threads
- if (!include_jvmti_agent_threads && jt->is_jvmti_agent_thread()) {
- continue;
- }
- // skip jni threads in the process of attaching
- if (!include_jni_attaching_threads && jt->is_attaching()) {
- continue;
- }
- instanceHandle h(cur_thread, (instanceOop) jt->threadObj());
- _threads_array->append(h);
- }
- }
我们也可以看到 JNI attach 的线程, 和 jvmti agent 的线程是不被统计在内的
3. ThreadInfo[] getThreadInfo 得到线程具体的堆栈信息
不论是传入要取的线程列表还是要取的所有的线程列表, 最后都会看到将取堆栈信息的任务交给了 vm thread 线程处理, 关于 vm thread 的作用可以参考本人的其他博客.
- // Obtain thread dumps and thread snapshot information
- VM_ThreadDump op(dump_result,
- thread_handle_array,
- num_threads,
- max_depth, /* stack depth */
- with_locked_monitors,
- with_locked_synchronizers);
- VMThread::execute(&op);
a. vm thread 去遍历所有线程的信息, 由于是单线程处理, 如果线程数量多的话是会影响到性能的, 因为在扫描堆栈过程中, 是在 softpoint 的状态.
b. ThreadDumpResult dump_result(num_threads); 使用 ThreadDumpResult 去存储 ThreadSnapshot 而保证不会被 gc, 因为从 vm thread 抓取线程结束, 在填充 threadinfo 的时候还是会发生 gc.
4. 锁的细节显示
在函数 dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers) 里有 2 个参数 lockedMonitor, 和 lockedSynchronizer
而这两个参数分别控制两种锁 ThreadInfo .getLockedMonitors() 和 ThreadInfo.getLockedSynchronizers()
a. Monitor 锁
就是我们传统使用的 synchronized(Object obj),
可以通过 MonitorInfo[] 得到具体的锁的数量和信息
b. Locked ownable synchronizers 锁
常指的 ReentrantLock 和 ReentrantReadWriteLock 锁
通过得到 LockInfo[] 可以得到具体的类, 锁的数量和信息
来源: http://www.bubuko.com/infodetail-2862916.html