深度探索JFR - JFR详细介绍与生产问题定位落地 - 3.

2020-04-03  本文已影响0人  干货满满张哈希

Event 采集详细配置

目前,JDK 11 一共有136个 Event 采集配置。这里会比较详细的去看每一个Event,并说明基本应用,建议配置。如果 default.jfc 中没有打开或者需要修改的配置,会将配置文件代码发出来。

1. JFR 相关 Event

一共4个 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 以及默认配置:

这两个的采集,对性能影响比较大,不能长期跑。尤其是在启用堆栈收集后,影响就更大了。一般考虑动态打开。

一般应用:

  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:

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:

我建议不用开启这两个 EVent 的采集,因为 Exception 我们可以通过日志分析, Error 一般框架都会有一些,一般与为我们的业务无关。

上一篇 下一篇

猜你喜欢

热点阅读