歡迎光臨
每天分享高質量文章

JVM原始碼分析之perfData檔案的建立

背景

看泉子的一篇文章:JVM原始碼分析之Jstat工具原理完全解讀 – 你假笨 裡提到了兩個JVM引數,可以控制perfdata檔案是否共享,取用泉子對這兩個引數的解釋:

  • UsePerfData:如果關閉了UsePerfData這個引數,那麼jvm啟動過程中perf memory都不會被建立,預設情況是是開啟的

  • PerfDisableSharedMem:該引數決定了儲存PerfData的記憶體是不是可以被共享,也就是說不管這個引數設定沒設定,jvm在啟動的時候都會分配一塊記憶體來存PerfData,只是說這個PerfData是不是其他行程可見的問題,如果設定了這個引數,說明不能被共享,此時其他行程將訪問不了該記憶體,這樣一來,譬如我們jps,jstat等都無法工作。預設這個引數是關閉的,也就是預設支援共享的方式

由於perfdata檔案時透過mmap共享的,因此考慮看下perfdata檔案的建立過程,看看跟mmap的MAPSHARED和MAPPRIVATE兩個標誌位是如何聯絡在一起的。perfdata檔案底層是使用mmap介面實現的,而mmap介面的引數中有關於記憶體可見性的兩個引數:MAPSHARED和MAPPRIVATE,如果JVM引數設定允許perfdata檔案共享,則使用MAP_SHARED標記。

原始碼分析

perfdata檔案在jvm啟動的時候創,在init.cpp檔案中:

  1. void vm_init_globals() {

  2.  check_ThreadShadow();

  3.  basic_types_init();

  4.  eventlog_init();

  5.  mutex_init();

  6.  chunkpool_init();

  7.  perfMemory_init();

  8. }

在perfMemory.cpp檔案中看下perfMemory_init()方法,

  1. void perfMemory_init() {

  2.  if (!UsePerfData) return;

  3.  PerfMemory::initialize();

  4. }

可以看出,如果UsePerfData引數設定為false,則直接傳回,不會建立perfdata檔案;接著看PerfMemory::initialize()方法,在這個方法裡會呼叫creatememoryregion(capacity);,用於申請perfdata的記憶體區域;creatememoryregion這個方法不同的平臺有不同的實現,我們這裡看linux平臺下的實現;

看下perfMemory_linux.cpp裡的程式碼,可以看到另一熟悉的JVM引數——PerfDisableSharedMem,如果這個引數設定為false,則會建立perfdata檔案,但是其他行程無法共享這塊記憶體,會導致jps、jstat等工具無法使用;

  1. // create the PerfData memory region

  2. //

  3. // This method creates the memory region used to store performance

  4. // data for the JVM. The memory may be created in standard or

  5. // shared memory.

  6. //

  7. void PerfMemory::create_memory_region(size_t size) {

  8.  if (PerfDisableSharedMem) {

  9.    // do not share the memory for the performance data.

  10.    _start = create_standard_memory(size);

  11.  }

  12.  else {

  13.    _start = create_shared_memory(size);

  14.    if (_start == NULL) {

  15.      // creation of the shared memory region failed, attempt

  16.      // to create a contiguous, non-shared memory region instead.

  17.      //

  18.      if (PrintMiscellaneous && Verbose) {

  19.        warning("Reverting to non-shared PerfMemory region.\n");

  20.      }

  21.      PerfDisableSharedMem = true;

  22.      _start = create_standard_memory(size);

  23.    }

  24.  }

  25.  if (_start != NULL) _capacity = size;

  26. }

首先看createsharedmemory(size)的實現:
createsharedmemory(size) ——> mmapcreateshared(size)——>mapAddress = (char)::mmap((char)0, size, PROTREAD|PROTWRITE, MAPSHARED, fd, 0);,在這裡看到了MAPSHARED標記。

然後看createstandardmemory(size)的實現,這裡並沒有跟之前猜想的一樣(用mmap方法建對映,傳入MAP_PRIVATE標記),而是使用了 os::reserve_memory(size)方法,用來分配一段堆記憶體。

  1. // Standard Memory Implementation Details

  2. // create the PerfData memory region in standard memory.

  3. //

  4. static char* create_standard_memory(size_t size) {

  5.  // allocate an aligned chuck of memory

  6.  char* mapAddress = os::reserve_memory(size);

  7.  if (mapAddress == NULL) {

  8.    return NULL;

  9.  }

  10.  // commit memory

  11.  if (!os::commit_memory(mapAddress, size, !ExecMem)) {

  12.    if (PrintMiscellaneous && Verbose) {

  13.      warning("Could not commit PerfData memory\n");

  14.    }

  15.    os::release_memory(mapAddress, size);

  16.    return NULL;

  17.  }

  18.  return mapAddress;

  19. }

至此可以確認兩個結論

  1. 建立shared記憶體,使用mmap,並傳入MAP_SHARED標記

  2. 建立standard記憶體,使用os::reserve_memory分配一段堆記憶體,這點跟之前猜想的不一樣,不過對這個方法還不是很熟悉,留著後面研究。



推薦一個我最近在學的JVM課程,來自Oracle高階研究員鄭宇迪在極客時間的JVM專欄,目前更新了16篇文章,我基本都跟下來了,質量值得信賴。


整個專欄將分為四大模組。

  1. 基本原理:剖析 Java 虛擬機器的執行機制,逐一介紹 Java 虛擬機器的設計決策以及工程實現;

  2. 高效實現:探索 Java 編譯器,以及內嵌於 Java 虛擬機器中的即時編譯器,幫助你更好地理解 Java 語言特性,繼而寫出簡潔高效的程式碼;

  3. 程式碼最佳化:介紹如何利用工具定位並解決程式碼中的問題,以及在已有工具不適用的情況下,如何打造專屬輪子;

  4. 虛擬機器黑科技:介紹甲骨文實驗室近年來的前沿工作之一 GraalVM。包括如何在 JVM 上高效執行其他語言;如何混搭這些語言,實現 Polyglot;如何將這些語言事前編譯(Ahead-Of-Time,AOT)成機器指令,單獨執行甚至嵌入至資料庫中執行。


贊(0)

分享創造快樂

© 2024 知識星球   網站地圖