Java JFR 民间指南 - 事件详解 - jdk.ObjectAllocationSample

默认配置(default.jfc):

配置 值 描述
enabled   true   默认启用  
throttle   150/s   每秒最多采集 150 个  
stackTrace   true   采集事件的时候,也采集堆栈  

采样配置(profile.jfc):

配置 值 描述
enabled   true   默认启用  
throttle   300/s   每秒最多采集 300 个  
stackTrace   true   采集事件的时候,也采集堆栈  
为何需要这个事件?

对于大部分的 JVM 应用,大部分的对象是在 TLAB 中分配的。如果 TLAB 外分配过多,或者 TLAB 重分配过多,那么我们需要检查代码,检查是否有大对象,或者不规则伸缩的对象分配,以便于优化代码。对于 TLAB 外分配和重分配分别有对应的事件:jdk.ObjectAllocationOutsideTLAB和jdk.ObjectAllocationInNewTLAB。但是这两个事件,如果不采集堆栈,则没有什么实际参考意义,如果采集堆栈的话,这两个事件数量非常大,尤其是出现问题的时候。那么采集堆栈的次数也会变得非常多,这样会非常影响性能。采集堆栈,是一个比较耗性能的操作,目前大部分的 Java 线上应用,尤其是微服务应用,都使用了各种框架,堆栈非常深,可能达到几百,如果涉及响应式编程,这个堆栈就更深了。JFR 考虑到这一点,默认采集堆栈深度最多是 64,即使是这样,也还是比较耗性能的。并且,在 Java 11 之后,JDK 一直在优化获取堆栈的速度,例如堆栈方法字符串放入缓冲池,优化缓冲池过期策略与 GC 策略等等,但是目前性能损耗还是不能忽视。所以,引入这个事件,减少对于堆栈的采集导致的消耗。

事件包含属性 属性 说明 举例
startTime   事件开始时间   10:16:27.718  
objectClass   触发本次事件的对象的类   byte[] (classLoader = bootstrap)  
weight   注意,这个不是对象大小,而是该线程距离上次被采集 jdk.ObjectAllocationSample 事件到这个事件的这段时间,线程分配的对象总大小   10.0 MB  
eventThread   线程   "Thread-0" (javaThreadId = 27)  
stackTrace   线程堆栈    
测试这个事件 package com.github.hashjang.jfr.test; import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordingFile; import sun.hotspot.WhiteBox; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.concurrent.TimeUnit; public class TestObjectAllocationSample { //对于字节数组对象头占用16字节 private static final int BYTE_ARRAY_OVERHEAD = 16; //分配对象的大小,1MB private static final int OBJECT_SIZE = 1024 * 1024; //要分配的对象个数 private static final int OBJECTS_TO_ALLOCATE = 20; //分配对象的 class 名称 private static final String BYTE_ARRAY_CLASS_NAME = new byte[0].getClass().getName(); private static final String INT_ARRAY_CLASS_NAME = new int[0].getClass().getName(); //测试的 JFR 事件名称 private static String EVENT_NAME = "jdk.ObjectAllocationSample"; //分配的对象放入这个静态变量,防止编译器优化去掉没有使用的分配代码 public static byte[] tmp; public static int[] tmp2; public static void main(String[] args) throws IOException, InterruptedException { //使用 WhiteBox 执行 FullGC,清楚干扰 WhiteBox whiteBox = WhiteBox.getWhiteBox(); whiteBox.fullGC(); Recording recording = new Recording(); //设置 throttle 为 1/s,也就是每秒最多采集一个 //目前 throttle 只对 jdk.ObjectAllocationSample 有效,还不算是标准配置,所以只能这样配置 recording.enable(EVENT_NAME).with("throttle", "1/s"); recording.start(); //main 线程分配对象 for (int i = 0; i < OBJECTS_TO_ALLOCATE; ++i) { //由于 main 线程在 JVM 初始化的时候分配了一些其他对象,所以第一次采集的大小可能不准确,或者采集的类不对,后面结果中我们会看到 tmp = new byte[OBJECT_SIZE - BYTE_ARRAY_OVERHEAD]; TimeUnit.MILLISECONDS.sleep(100); } //测试多线程分配对象 Runnable runnable = () -> { for (int i = 0; i < OBJECTS_TO_ALLOCATE; ++i) { tmp = new byte[OBJECT_SIZE - BYTE_ARRAY_OVERHEAD]; try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread = new Thread(runnable); Runnable runnable2 = () -> { for (int i = 0; i < OBJECTS_TO_ALLOCATE; ++i) { tmp2 = new int[OBJECT_SIZE - BYTE_ARRAY_OVERHEAD]; try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread2 = new Thread(runnable2); thread.start(); thread2.start(); long threadId = thread.getId(); long threadId2 = thread2.getId(); thread.join(); thread2.join(); recording.stop(); Path path = new File(new File(".").getAbsolutePath(), "recording-" + recording.getId() + "-pid" + ProcessHandle.current().pid() + ".jfr").toPath(); recording.dump(path); long size = 0; for (RecordedEvent event : RecordingFile.readAllEvents(path)) { if (!EVENT_NAME.equals(event.getEventType().getName())) { continue; } String objectClassName = event.getString("objectClass.name"); boolean isMyEvent = ( Thread.currentThread().getId() == event.getThread().getJavaThreadId() || threadId == event.getThread().getJavaThreadId() || threadId2 == event.getThread().getJavaThreadId() ) && ( objectClassName.equals(BYTE_ARRAY_CLASS_NAME) || objectClassName.equals(INT_ARRAY_CLASS_NAME) ); if (!isMyEvent) { continue; } System.out.println(event); } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspzgs.html