1. 程式人生 > >JVM源碼系列:ThreadMXBean 打出堆棧信息原理分析

JVM源碼系列:ThreadMXBean 打出堆棧信息原理分析

數量 extern rfc cto per ren mutex .get ont

我們通常會使用工具jstack 去跟蹤線程信息,其如何實現使用attach 的方式還是ptrace 的方式,這些可以去參考本人的博客的其他文章。

但這些方式都是外部使用的方式,如何直接使用java代碼得到當前進程的線程的信息,方便監控jvm的整個運行狀態,就不的不提到了ManagementFactory

通過調用方法

ThreadMXBean tmbean = ManagementFactory.getThreadMXBean();

通過得到 ThreadMXBean 可以得到非常多的thread信息,博客裏也主要提到幾個重要函數的實現

ThreadMXBean是個接口,主要實現都是在 ThreadImpl.java 裏實現。

1. getThreadCount()

在虛擬機裏會有ThreadService,裏面會有些計數器用於記錄總線程數,活著線程數目

  1. PerfCounter* ThreadService::_total_threads_count = NULL;
  2. PerfVariable* ThreadService::_live_threads_count = NULL;
  3. PerfVariable* ThreadService::_peak_threads_count = NULL;
  4. PerfVariable* ThreadService::_daemon_threads_count = NULL
    ;
  5. volatile int ThreadService::_exiting_threads_count = 0;
  6. volatile int ThreadService::_exiting_daemon_threads_count = 0;


當線程創建,消亡都會調用 ThreadService 的方法來對計數器加減,這樣就能直接得到線程數目的狀態,而不需要去遍歷線程鏈表。

2. getAllThreadIds()

這個實現也比較簡單,直接掃描線程列表,就可以得到每個java 的線程id, 在掃描過程中使用了鎖,鎖住了線程鏈表。 但因為從native代碼到java代碼中沒有鎖結構,得到線程的列表只能表示當時的狀態,當得到id的時候並不能保證該線程依然存活。

  1. ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread,
  2. bool include_jvmti_agent_threads,
  3. bool include_jni_attaching_threads) {
  4. assert(cur_thread == Thread::current(), "Check current thread");
  5. int init_size = ThreadService::get_live_thread_count();
  6. _threads_array = new GrowableArray<instanceHandle>(init_size);
  7. MutexLockerEx ml(Threads_lock);
  8. for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
  9. // skips JavaThreads in the process of exiting
  10. // and also skips VM internal JavaThreads
  11. // Threads in _thread_new or _thread_new_trans state are included.
  12. // i.e. threads have been started but not yet running.
  13. if (jt->threadObj() == NULL ||
  14. jt->is_exiting() ||
  15. !java_lang_Thread::is_alive(jt->threadObj()) ||
  16. jt->is_hidden_from_external_view()) {
  17. continue;
  18. }
  19. // skip agent threads
  20. if (!include_jvmti_agent_threads && jt->is_jvmti_agent_thread()) {
  21. continue;
  22. }
  23. // skip jni threads in the process of attaching
  24. if (!include_jni_attaching_threads && jt->is_attaching()) {
  25. continue;
  26. }
  27. instanceHandle h(cur_thread, (instanceOop) jt->threadObj());
  28. _threads_array->append(h);
  29. }
  30. }


我們也可以看到 JNI attach 的線程,和jvmti agent的線程是不被統計在內的

3. ThreadInfo[] getThreadInfo 得到線程具體的堆棧信息

不論是傳入要取的線程列表還是要取的所有的線程列表,最後都會看到將取堆棧信息的任務交給了vm thread 線程處理,關於vm thread的作用可以參考本人的其他博客。

  1. // Obtain thread dumps and thread snapshot information
  2. VM_ThreadDump op(dump_result,
  3. thread_handle_array,
  4. num_threads,
  5. max_depth, /* stack depth */
  6. with_locked_monitors,
  7. with_locked_synchronizers);
  8. 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[] 可以得到具體的類,鎖的數量和信息

JVM源碼系列:ThreadMXBean 打出堆棧信息原理分析