深度探索JFR - JFR詳細介紹與生產問題定位落地 - 3. 各種Event詳細說明與JVM調優策略(1)

Event 採集詳細配置

目前,JDK 11 一共有136個 Event 採集配置。這裏會比較詳細的去看每一個Event,並說明基本應用,建議配置。如果 default.jfc 中沒有打開或者需要修改的配置,會將配置文件代碼發出來。

1. JFR 相關 Event

一共4個 Event,但是需要關心的就下面這兩個

  • Data Loss:數局丟失 Event,當有數據發生丟失時,會有這個Event 進行記錄。包括開始時間,Amount(本次丟失多少事件),Total(一共丟失多少事件)
  • Recording Setting:記錄詳細配置採集 Event,會在每次產生新的 Data Chunk 的時候採集一次所有的 Event 的詳細配置並記錄到這個 Event 中。

這些在 default.jfc 中默認打開

2. JAVA 應用相關

2.1. TLAB 相關

衆所周知,TLAB (Thread Local Allocation Buffer)目的是爲類進行內存快速分配。堆內存所有線程共享訪問,所以在堆內存上面分配對象,就會鎖定整個堆,這樣效率太低。TLAB 是位於堆內存上面的一塊內存區域,在爲每個線程分配 TLAB 的時候纔會鎖定堆(G1 是CAS分配)。分配對象的時候,優先從線程的 TLAB 上分配,這樣就不用和其他線程同步。當對象比較大的時候,例如對於 G1 來說, HeapRegionSize 配置大小的一半以上的對象就被認爲是大對象,大對象的分配不會發生在 TLAB,不在 TLAB 發生的對象分配會涉及到線程同步。

這是比較籠統的看法,針對於 G1,這個算法更加複雜。爲了能說明 JFR 相關事件的意義,這裏繼續深入一下關於 G1 TLAB 相關原理。

創建一個對象時:

  1. 首先嚐試從線程現有的TLAB空間分配內存
  2. 如果剩餘空間不足,查看是否能分配一個新的TLAB,再分配內存給對象
  3. TLAB 的實現內部,每個線程維護一個 refill_waste 的變量,根據這個變量的值決定是否能分配一個新的TLAB。這個變量會根據一定算法隨着線程的運行不斷變化
  4. 同時, 每個線程的 TLAB 大小也是隨着線程運行不斷變化的
  5. 當 TLAB 剩餘空間不足時,查看當前 TLAB 的剩餘大小,如果小於 refill_waste 當前值,則認爲 TLAB 該擴容了,需要分配一個新的TLAB,這時候,JFR 會產生一條 ObjectAllocationInNewTLAB Event 記錄;如果不小於,則認爲這個 TLAB 還不算滿,當前這個對象直接走堆上內存分配,不從 TLAB 分配,這時會產生一條 ObjectAllocationOutsideTLAB Event 記錄。

涉及的 Event 以及默認配置:

  • ObjectAllocationInNewTLAB: TLAB 擴容時產生的 Event
    • 在 default.jfc 中默認沒有打開,可以通過嚮導配置 memory-profiling 調爲 memory-profiling-enabled-medium 打開
    • 也可以用高級配置配置這個 Event 是否採集,以及堆棧是否採集
      -採集內容包括:時間,線程,本次需要分配內存大小,對象類型,當前 TLAB 大小
  • ObjectAllocationOutsideTLAB:
    • 在 default.jfc 中默認沒有打開,可以通過嚮導配置 memory-profiling 調爲 memory-profiling-enabled-medium 打開
    • 也可以用高級配置配置這個 Event 是否採集,以及堆棧是否採集
    • 採集內容包括:時間,線程,本次需要分配內存大小,對象類型

這兩個的採集,對性能影響比較大,不能長期跑。尤其是在啓用堆棧收集後,影響就更大了。一般考慮動態打開。

一般應用:

  1. 對於上一節裏面需要確定 HeapRegionSize 大小的時候,可以考慮採集一段時間內的 ObjectAllocationOutsideTLAB Event,查看最大需要的內存大小是多少。
  2. 如果考慮通過減少內存分配,來減少 GC,或者定位大內存分配代碼位置,可以打開這兩個 Event 的採集,查看造成這些事件的熱點堆棧是哪裏,以此優化代碼。

配置打開示例:

    <event name="jdk.ObjectAllocationInNewTLAB">
      <setting name="enabled">true</setting>
      <setting name="stackTrace">true</setting>
    </event>

    <event name="jdk.ObjectAllocationOutsideTLAB">
      <setting name="enabled">true</setting>
      <setting name="stackTrace">true</setting>
    </event>

事件 jmc 查看示例:

image

image

2.2. 文件操作相關

主要涉及三個 Event:

  • FileForce:強制寫的時候,會產生這個 Event
  • FileRead:文件讀的時候,會產生這個 Event
  • FileWrite:文件寫的時候,會產生這個 Event

FileChannel舉例:

try (RandomAccessFile reader = new RandomAccessFile("src/test/resources/test_read.in", "rw");
    FileChannel channel = reader.getChannel();
    ByteArrayOutputStream out = new ByteArrayOutputStream()) {
    int bufferSize = 1024;
    ByteBuffer buff = ByteBuffer.allocate(bufferSize);
    channel.read(buff); //產生 FileRead
    channel.write(buff); // 產生 FileWrite
    
    channel.force(true); // 強制不寫入高速緩存,直接寫入磁盤文件
    channel.write(buff); // 產生 FileForce
}

這三個事件配置可以採集堆棧,設置採集時間閾值。在 default.jfc 中,這三個事件默認都是採集的,堆棧採集打開,並且閾值是20ms。如果你的應用只是打日誌用到了文件,那個這個默認配置就很足夠了。尤其是對於 Log4j2 的異步日誌,這個閾值是夠用的。如果你的應用需要高頻操作文件,例如 RocketMQ 的 日誌文件(基於 mmap)的,則這個閾值最好改成10ms,因爲對文件寫入讀取速度要求更高。

2.3. 異常與錯誤相關

主要涉及兩個 Event:

  • Java Error Event:當有 Error 被 throw 時,會產生這個event, default.jfc 中默認開啓這個採集,並且包括堆棧(配置項是 JavaErrorThrow)
  • Java Exception Event:當有 Exception 被 throw 時,會產生這個event,default.jfc 中默認不開啓這個採集,並且包括堆棧(配置項是 JavaExceptionThrow)

我建議不用開啓這兩個 EVent 的採集,因爲 Exception 我們可以通過日誌分析, Error 一般框架都會有一些,一般與爲我們的業務無關。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章