關於【JVM Performance Counters】的實現

    在Java內置工具包tools.jar(一)sun.tools.jstack.Jps中咱們談到,jps工具的數據來源自hsperfdata_xxx目錄下的文件中。其實,除了jps還有jstat以及基於此命令之上提供的jstatd、visualgc工具,數據來源均基於JVM Performance Counters輸出的文件中,在JVM中該文件被稱爲PerfData。PerfData中記錄了JVM運行時刻gc、編譯器、classLoder、操做系統、線程等的狀態數據,對於分析jvm運行狀態很是重要。
      從頭開始,在JVM初始化函數中,咱們看到初始化全局數據中執行了perfMemory_init()方法:html

void vm_init_globals() {
  check_ThreadShadow();
  basic_types_init();
  eventlog_init();
  mutex_init();
  chunkpool_init();
  perfMemory_init();
}
void perfMemory_init() {
  if (!UsePerfData) return;
  PerfMemory::initialize();
}
// create the PerfData memory region
//
// This method creates the memory region used to store performance
// data for the JVM. The memory may be created in standard or
// shared memory.
//
void PerfMemory::create_memory_region(size_t size) {

  if (PerfDisableSharedMem) {
    // do not share the memory for the performance data.
    _start = create_standard_memory(size);
  }
  else {
    _start = create_shared_memory(size);
    if (_start == NULL) {

      // creation of the shared memory region failed, attempt
      // to create a contiguous, non-shared memory region instead.
      //
      if (PrintMiscellaneous && Verbose) {
        warning("Reverting to non-shared PerfMemory region.\n");
      }
      PerfDisableSharedMem = true;
      _start = create_standard_memory(size);
    }
  }

  if (_start != NULL) _capacity = size;
}

也就是PerfData的初始化,其中UsePerfData控制了是否使用該特性。java

-XX:+UsePerfData:Enables the perfdata feature. This option is enabled by default to allow JVM monitoring and performance testing. Disabling it suppresses the creation of the hsperfdata_userid directories. To disable the perfdata feature, specify -XX:-UsePerfData.jvm

PerfMemory的初始化在不一樣的操做系統有不一樣的實現,咱們仍以Linux爲例,jvm須要申請一分內存在存儲這份數據,從代碼中能夠看到咱們有兩種選擇:create_shared_memory 和create_standard_memory,共享模式的內存即便用文件hsperfdata_xxx來給不一樣的用戶/進程來讀取,該文件同時使用mmap的方式將內容映射到內存中。私有模式即申請普通內存,此時外部進程沒法讀取內存數據,因此基於此數據的命令都將失效(jps/jstat等),另外須要注意的是在共享模式申請內存(同時能夠理解爲建立文件)失敗時會轉換爲私有模式。這裏又來了一個JVM參數PerfDisableSharedMem,默認咱們使用的jdk都是false使用共享模式,但咱們仍可使用參數來控制這個行爲。函數

product(bool, PerfDisableSharedMem, false,"Store performance data in standard memory")

        那麼,存儲性能數據的內存空間已經申請完畢,接下來如何收集數據?Performance Counters定義了一些它數據來源的nameSpace,以下代碼所示。工具

/* jvmstat global and subsystem counter name space - enumeration value
 * serve as an index into the PerfDataManager::_name_space[] array
 * containing the corresponding name space string. Only the top level
 * subsystem name spaces are represented here.
 */
enum CounterNS {
  // top level name spaces
  JAVA_NS,
  COM_NS,
  SUN_NS,
  // subsystem name spaces
  JAVA_GC,              // Garbage Collection name spaces
  COM_GC,
  SUN_GC,
  JAVA_CI,              // Compiler name spaces
  COM_CI,
  SUN_CI,
  JAVA_CLS,             // Class Loader name spaces
  COM_CLS,
  SUN_CLS,
  JAVA_RT,              // Runtime name spaces
  COM_RT,
  SUN_RT,
  JAVA_OS,              // Operating System name spaces
  COM_OS,
  SUN_OS,
  JAVA_THREADS,         // Threads System name spaces
  COM_THREADS,
  SUN_THREADS,
  JAVA_PROPERTY,        // Java Property name spaces
  COM_PROPERTY,
  SUN_PROPERTY,
  NULL_NS,
  COUNTERNS_LAST = NULL_NS
};

在JVM運行期間,經過調用PerfDataManager保持時刻記錄這些運行數據,它提供一系列的接口供收集性能數據,咱們以查找調用方的方式能夠看到StatSampler是觸發數據收集的函數,它在Thread::create_vm()時建立的一個守護線程,在engage()中看到它的初始化伴隨一個JVM參數PerfDataSamplingInterval:默認50秒執行一次去counter數據。性能

product(intx, PerfDataSamplingInterval, 50 /*ms*/,"Data sampling interval in milliseconds")

至此,性能數據就源源不斷的採集下來,咱們回顧一下整個流程:spa

一、啓動jvm,初始化perfData內存。包含共享內存和普通內存申請。共享內存涉及映射文件的建立位置等邏輯。
二、一個線程啓動,均會初始化StatSampler,以PerfDataSamplingInterval的頻率採集數據至perfData中。
三、perfData彙總數據存儲在內存中,在共享內存模式下以mmap方式映射到文件中。
四、文件可被其餘進程/用戶讀取。

以數據流的方式來理解,其實並無什麼高深之處,數據的收集須要各個運行模塊配合採集。這樣就完成了Performance Counters的功能,這些數據可以在咱們排查性能問題、處理故障時發揮巨大做用。操作系統

相關文章
相關標籤/搜索