JVM 相关

Stella981
• 阅读 801

什么是 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 中

test/lib/sun/hotspot/WhiteBox.java

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

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

src/hotspot/share/prims/whitebox.cpp

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 源码是: src/hotspot/share/runtime/arguments.cpp

// -bootclasspath:
} else if (match_option(option, "-Xbootclasspath:", &tail)) {
    jio_fprintf(defaultStream::output_stream(),
      "-Xbootclasspath is no longer a supported option.\n");
    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.\n");
    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 API 的使用场景

  1. JDK 框架源码在测试的时候,需要用 WhiteBox API 进行测试
  2. 压测 JVM 的时候,需要模拟一些场景,需要用 WhiteBox API 执行一些底层操作,例如强制 full GC。比较有名的就是 jcstress 对 JVM 进行压测就用到了 WhiteBox API
  3. 一些性能极限场景,需要用到 WhiteAPI 进行模拟,压测
  4. 学习 JVM 原理的时候会用到。

最后,在我的系列专栏通过 JFR 与日志深入探索 JVM中,也会经常用到 WhiteBox API 模拟各种 JFR 事件,帮助大家更好的理解 JVM 底层原理和性能瓶颈定位,JVM 调优等等,欢迎大家订阅关注~

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java基础知识随身记
2018年11月12日20:51:35一、基础知识:1、JVM、JRE和JDK的区别:JVM(JavaVirtualMachine):java虚拟机,用于保证java的跨平台的特性。  java语言是跨平台,jvm不是跨平台的。JRE(JavaRuntimeEnvironment):java的运行环境,包括jvmjava的核心类
Stella981 Stella981
3年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这