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

简介: Java JFR 民间指南 - 事件详解 - jdk.ObjectAllocationSample(上)

对象分配采样:jdk.ObjectAllocationSample


引入版本:Java 16

相关 ISSUEIntroduce JFR Event Throttling and new jdk.ObjectAllocationSample event (enabled by default)


各版本配置:


Java 16

默认配置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.ObjectAllocationOutsideTLABjdk.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);
        }
    }
}
相关文章
|
14天前
|
Java 开发者 Spring
java springboot监听事件和处理事件
通过上述步骤,开发者可以在Spring Boot项目中轻松实现事件的发布和监听。事件机制不仅解耦了业务逻辑,还提高了系统的可维护性和扩展性。掌握这一技术,可以显著提升开发效率和代码质量。
77 33
|
4月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
110 2
|
6天前
|
算法 Java 编译器
深入理解 Java JDK —— 让我们从基础到进阶
JDK(Java Development Kit)是 Java 开发的核心工具包,包含编译、运行和调试 Java 程序所需的所有工具和库。它主要由 JVM(Java 虚拟机)、JRE(Java 运行时环境)和 Java 核心类库组成。JVM 是跨平台运行的基础,负责字节码的加载、执行和内存管理;JRE 提供运行 Java 应用的环境;核心类库则提供了丰富的 API 支持。通过编写、编译和运行一个简单的 Java 程序,可以深入理解 JDK 的工作原理。此外,JDK 还提供了 JIT 编译、垃圾回收优化和并发工具包等高级功能,帮助开发者提高程序性能和稳定性。
61 10
|
16天前
|
Java 开发者 Spring
java springboot监听事件和处理事件
通过上述步骤,开发者可以在Spring Boot项目中轻松实现事件的发布和监听。事件机制不仅解耦了业务逻辑,还提高了系统的可维护性和扩展性。掌握这一技术,可以显著提升开发效率和代码质量。
51 13
|
20天前
|
Java Spring
Java Spring Boot监听事件和处理事件
通过上述步骤,我们可以在Java Spring Boot应用中实现事件的发布和监听。事件驱动模型可以帮助我们实现组件间的松耦合,提升系统的可维护性和可扩展性。无论是处理业务逻辑还是系统事件,Spring Boot的事件机制都提供了强大的支持和灵活性。希望本文能为您的开发工作提供实用的指导和帮助。
72 15
|
22天前
|
Java 开发者 Spring
Java Springboot监听事件和处理事件
通过这些内容的详细介绍和实例解析,希望能帮助您深入理解Spring Boot中的事件机制,并在实际开发中灵活应用,提高系统的可维护性和扩展性。
54 7
|
2月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
2月前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
134 1
|
2月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
117 1
|
3月前
|
缓存 Java Maven
java: 警告: 源发行版 11 需要目标发行版 11 无效的目标发行版: 11 jdk版本不符,项目jdk版本为其他版本
如何解决Java项目中因JDK版本不匹配导致的编译错误,包括修改`pom.xml`文件、调整项目结构、设置Maven和JDK版本,以及清理缓存和重启IDEA。
79 1
java: 警告: 源发行版 11 需要目标发行版 11 无效的目标发行版: 11 jdk版本不符,项目jdk版本为其他版本