当前位置: 首页 > news >正文

通过 JFR 与日志深入探索 JVM - 调试 JVM 的工具 WhiteBox API

在之后的 JFR 事件学习以及调试的过程中,我们会经常用到 WhiteBox API 来触发 JVM 的一些机制或者临界点。例如强制 JVM 现在立刻进行 FullGC 等等。

什么是 WhiteBox API

WhiteBox API 是 HotSpot VM 自带的白盒测试工具,将内部的很多核心机制的 API 暴露出来,用于白盒测试 JVM,压测 JVM 特性,以及辅助学习理解 JVM 并调优参数。WhiteBox API 是 Java 7 引入的,目前 Java 8 LTS 以及 Java 11 LTS(其实是 Java 9+ 以后的所有版本,这里只关心 LTS 版本,Java 9 引入了模块化所以 WhiteBox API 有所变化)都是有的。但是默认这个 API 并没有编译在 JDK 之中,但是他的实现是编译在了 JDK 里面了。所以如果想用这个 API,需要用户自己编译需要的 API,并加入 Java 的 BootClassPath 并启用 WhiteBox API。

WhiteBox API 如何实现的

WhiteBox API 是一个 Java 类,位于 JDK 的测试包中,默认没有编译进标准发行版的 JDK 中

[这里是代码001]

package sun.hotspot;
public class WhiteBox {
  //仅举两个例子,省略其他 api 以及代码
  // Force Young GC
  public native void youngGC();
  // Force Full GC
  public native void fullGC();
}

可以看出,其实里面的所有 API 都是 JNI 调用,具体实现是:

[这里是代码003]

WB_ENTRY(void, WB_FullGC(JNIEnv* env, jobject o))
  Universe::heap()->soft_ref_policy()->set_should_clear_all_soft_refs(true);
  Universe::heap()->collect(GCCause::_wb_full_gc);
#if INCLUDE_G1GC
  if (UseG1GC) {
    // Needs to be cleared explicitly for G1
    Universe::heap()->soft_ref_policy()->set_should_clear_all_soft_refs(false);
  }
#endif // INCLUDE_G1GC
WB_END

WB_ENTRY(void, WB_YoungGC(JNIEnv* env, jobject o))
  Universe::heap()->collect(GCCause::_wb_young_gc);
WB_END

{CC"youngGC",  CC"()V",                             (void*)&WB_YoungGC },
{CC"fullGC",   CC"()V",                             (void*)&WB_FullGC },

//省略其他代码

可以看出,JNI 调用实现直接调用了底层 JVM 的相关接口,相当于把 JVM 的一些关键机制暴露出来,用于白盒测试。但是如之前所说,JDK 发行版没有包括 test 下的测试代码,也就是 WhiteBox API 所在的 jar 包并没有打进默认的 JDK 中。这就需要我们自己编译一下这个代码。

什么是 BootClassPath

Java 内有三种不同的类加载器:应用类加载器(application classloader),扩展类加载器(extension classloader)还有根类加载器(bootstrap classloader)

  • 应用类加载器,加载我们classpath目录下的所有类文件
  • 扩展类加载器,加载标准 Java 类库扩展的类,就是你的jre目录下的/lib/ext目录下的所有类
  • 根类加载器(bootstrap classloader),扫描 BootClassPath 下的 标准 Java 类库的类加载器。标准 Java 类库限制了一些包路径的类,必须通过根类加载器加载。

对于 WhiteBox API,由于是他的包为sun.hotspot,普通的类加载器是不能加载这个包路径的类的,需要通过根类加载器加载。

怎么指定 BootClassPath

在 Java 8,通过 -Xbootclasspath: 或者 -Xbootclasspath/p:指定,例如:

-Xbootclasspath:/home/project/whitebox.jar
-Xbootclasspath/p:/home/project/whitebox.jar

在 Java 9 之后的版本,这两个参数已经过期了,需要改成-Xbootclasspath/a:,例如:

-Xbootclasspath/a:/home/project/whitebox.jar

否则会报错-Xbootclasspath is no longer a supported option.

这里对应的 JDK 源码是:
[这里是代码012]

// -bootclasspath:
} else if (match_option(option, "-Xbootclasspath:", &tail)) {
    jio_fprintf(defaultStream::output_stream(),
      "-Xbootclasspath is no longer a supported option.
");
    return JNI_EINVAL;
// -bootclasspath/a:
} else if (match_option(option, "-Xbootclasspath/a:", &tail)) {
  //将参数添加到 bootclasspath 中
  Arguments::append_sysclasspath(tail);
// -bootclasspath/p:
} else if (match_option(option, "-Xbootclasspath/p:", &tail)) {
    jio_fprintf(defaultStream::output_stream(),
      "-Xbootclasspath/p is no longer a supported option.
");
    return JNI_EINVAL;
}

使用 WhiteBox API

1. 编译 WhiteBox API

https://github.com/openjdk/jdk/tree/master/test/lib路径下的sun目录取出,编译成一个 jar 包,名字假设是 whitebox.jar

2. 编写测试程序

whitebox.jar 添加到你的项目依赖,之后写代码

public static void main(String[] args) throws Exception {
        WhiteBox whiteBox = WhiteBox.getWhiteBox();
        //获取 ReservedCodeCacheSize 这个 JVM flag 的值
        Long reservedCodeCacheSize = whiteBox.getUintxVMFlag("ReservedCodeCacheSize");
        System.out.println(reservedCodeCacheSize);
        //打印堆内存各项指标
        whiteBox.printHeapSizes();
        //执行full GC
        whiteBox.fullGC();
        
        //保持进程不退出,保证日志打印完整
        Thread.currentThread().join();
}

3. 启动程序查看效果

使用启动参数 -Xbootclasspath/a:/home/project/whitebox.jar -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xlog:gc 启动程序。其中前三个 Flag 表示启用 WhiteBox API,最后一个表示打印 GC info 级别的日志到控制台。

我的输出:

[0.025s][info][gc] Using G1
251658240
Minimum heap 8388608 Initial heap 268435456 Maximum heap 4276092928 Space alignment 2097152 Heap alignment 2097152
[0.899s][info][gc] GC(0) Pause Full (WhiteBox Initiated Full GC) 5M->0M(20M) 45.183ms

至此,我们就准备好了 WhiteBox 调试环境,接下来,我们开始逐一分析 JFR 各个事件。

相关文章:

  • [毕业设计源代码]精品基于SSM的线上点餐系统[包运行成功]
  • C/C++创建tty,创建终端
  • End of line spacing
  • C 长度为0 的数组
  • javaweb JAVA JSP学生信息档案管理系统JSP学生管理系统JSP学生档案管理系统JSP学生信息管理系统
  • 天玑810和天玑900哪个好 天玑810和天玑900差距
  • 如何管理现代信息化机房
  • 单声道D类音频放大器 CS8631E 特点及应用
  • 【Python】第九课 类和对象
  • WPF中加载GIF
  • 快来直播带你了解中国互联网大厂布局元宇宙现状如何?
  • 配置JVM堆栈大小
  • 新美域杂志新美域杂志社新美域编辑部2022年第6期目录
  • 安卓毕业设计源码基于Uniapp+SSM实现的新闻APP
  • Qt报错: error: C2001: 常量中有换行符,解决QT运行时有中文乱码
  • ----------
  • 「面试题」如何实现一个圣杯布局?
  • ES2017异步函数现已正式可用
  • mac修复ab及siege安装
  • SSH 免密登录
  • vue-router 实现分析
  • XForms - 更强大的Form
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 区块链共识机制优缺点对比都是什么
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 优化 Vue 项目编译文件大小
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • No resource identifier found for attribute,RxJava之zip操作符
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • #每天一道面试题# 什么是MySQL的回表查询
  • (2)MFC+openGL单文档框架glFrame
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (6)STL算法之转换
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (二)linux使用docker容器运行mysql
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (强烈推荐)移动端音视频从零到上手(上)
  • (算法设计与分析)第一章算法概述-习题
  • (一) springboot详细介绍
  • (一)基于IDEA的JAVA基础10
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • . NET自动找可写目录
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .NET 中使用 Mutex 进行跨越进程边界的同步
  • .net连接oracle数据库
  • .net网站发布-允许更新此预编译站点
  • [ IOS ] iOS-控制器View的创建和生命周期
  • [2009][note]构成理想导体超材料的有源THz欺骗表面等离子激元开关——
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [bbk5179]第66集 第7章 - 数据库的维护 03
  • [C语言]编译和链接
  • [English]英语积累本
  • [Flutter]WindowsPlatform上运行遇到的问题总结