JVM

运行时选项

纯解释执行

-Xint

PrintCompilation选项

请参考 About PrintComiplation

PrintAssembly选项

请参考 PrintAssembly

CompileCommand选项

可以控制 JVM 编译指定的函数:

-XX:CompileCommand=compileonly,<ClassName>.<function|*>

PrintInlining选项

查看函数 inline 情况

PrintIntrinsics选项

打印调用 intrinsic 情况

TieredStopAtLevel=1选项

不开启 C2 编译

打印所有设置的选项

-XX:+PrintFlagsFinal

编译

主要参考:Building the JDK

  • 构建可调试版本

    bash ./configure --with-debug-level=fastdebug --with-native-debug-symbols=internal
    make images CONF=fastdebug
    
  • 指定安装目录

    ./configure --prefix=<path to install>
    make install
    
  • 编译时忽略告警错误

    --disable-warnings-as-errors

  • 构建最小版本

    通过构建最小版本可以使几乎所有的特性宏复位:--with-target-bits=32 --with-jvm-variants=minimal1,这里最小版本的构建只能在 32 位机器(工具链)上进行。另外,高版本支持的特性开关更加全面,所以没有必要通过构建最小版本来复位特性宏。

基本测试

主要参考:Testing the JDK

JTReg

  • 在 configure 中指定 jtreg

    设置环境变量 JT_HOME 为 jtreg 的安装路径,或者添加选项-with-jtreg=<path to jtreg home>

  • 执行测试

    make test TEST="tier1"
    
  • 选项参考

    jtreg: Command Line Options

    一般单独执行某个 jtreg 用例可以使用如下命令:

    jtreg -va -jdk:<path to java home> -javaoptions:<"java options separated by spaces">
    
  • Cannot determine version of java to run jtreg

    需要指定运行 jtreg 必要的 JDK

    export JT_JAVA=/path/to/java_home
    

测试新加指令

  • 生成测试数据

    python test/hotspot/gtest/aarch64/aarch64-asmtest.py | expand > test/hotspot/gtest/aarch64/asmtest.out.h

  • 运行 gtest:

    bash configure --with-debug-level=slowdebug --with-native-debug-symbols=internal --with-gtest=<path to getest>

    make test TEST="gtest:AssemblerAArch64" or make test-only TEST="gtest:AssemblerAArch64"

JMH

  • 生成 jmh 相关 jar 包

    sh make/devkit/createJMHBundle.sh

  • 在 configure 中指定 jmh

    --with-jmh=build/jmh/jars

  • 运行 jmh:

    比如:make test TEST="micro:java.lang.reflect" MICRO="VM_OPTIONS=-XX:TieredStopAtLevel=1" CONF=fastdebug

  • 使用 perfasm 来对测试用例进行基于 perf event 的采样,输入下面的命令,通过-prof perfasm:events=cycles,instructions,cache-misses对cycles,instructions,cache-misses三个 event 进行采样,以第一个event为排序标准:

    java -jar target/simd.jar -wi 10 -i 10 -f 1 -prof perfasm:events=cycles,instructions,cache-misses -o report_415.jmh -jvm /home/miaozhuojun/share/JVM/build/linux-aarch64-normal-server-fastdebug_jianye/images/j2sdk-image/bin/java SumProduct

ASAN 内存泄漏检测

在测试前需要添加环境变量:

export ASAN_OPTIONS="handle_segv=0:halt_on_error=0:detect_leaks=0"

否则会报错:

cannot determine version for JDK:

然后配置 configure:

bash $PWD/configure --with-extra-cflags="$AsanCompilerOpts" --with-extra-cxxflags="$AsanCompilerOpts" --with-extra-ldflags="$AsanCompilerOpts -Wl" --with-debug-level=fastdebug --with-native-debug-symbols=internal --enable-unlimited-crypto --with-stdc++lib=dynamic --with-boot-jdk=<boot_jdk> --with-milestone=fcs --with-extra-cflags=-fno-aggressive-loop-optimizations

JVM 字节码

JVM 内存相关选项

  • -Xms<xxxk/m/g> 表示 JVM 初始化时堆的大小。

  • -Xmx<xxxk/m/g> 表示 JVM 堆可以分配到的最大值。

  • -Xmn<xxxk/m/g> 表示 JVM 堆区新生代的大小

打印 JIT 生成汇编指令

  • 使用 hsdis 解析机器指令到汇编指令

    cp ~/share/hsdis/hsdis-aarch64.so ../jdk/build/linux-aarch64-server-fastdebug/images/jdk/lib/server/

  • 开启 JVM 选项打印汇编指令

    -XX:CompileCommand=print,java/lang/String.equals or -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+PrintOptoAssembly

    CompileCommand含义:

    Specifies a command to perform on a method. 该参数用于定制编译需求,比如过滤某个方法不做 JIT 编译 若未指定方法描述符,则对全部同名方法执行命令操作,具体如何指定见下文『举例』 可使用星号通配符(*)指定类或方法,具体如何使用见下文『举例』 该参数可多次指定,或使用 换行符(\n)分隔参数后的多个命令 解析完该命令后,JIT 编译器会读取 .hotspot_compiler 文件中的命令,该参数也可写在 .hotspot_compiler 文件中 可使用 -XX:CompileCommandFile 指定 .hotspot_compiler 文件为其他文件

    用法:

    -XX:CompileCommand=command,method[,option]

    命令:

    exclude,跳过编译指定的方法

    compileonly,只编译指定的方法

    inline/dontinline,设置是否内联指定方法

    print,打印生成的汇编代码

    break,JVM 以 debug 模式运行时,在方法编译开始处设置断点

    quiet,不打印在此命令之后、通过 -XX:CompileCommand 指定的编译选项

    log,记录指定方法的编译日志,若未指定,则记录所有方法的编译日志

    其他命令,option,help

    举例:

    1. 设置编译器跳过编译 com.jvmpocket.Dummy 类 test 方法的 4 种写法:
        -XX:CompileCommand=exclude,com/jvmpocket/Dummy.test
        -XX:CompileCommand=exclude,com/jvmpocket/Dummy::test
        -XX:CompileCommand=exclude,com.jvmpocket.Dummy::test
        -XX:CompileCommand="exclude com/jvmpocket/Dummy test
      
    2. 设置编译器只跳过编译 java.lang.String 类 int indexOf(String) 方法:

    -XX:CompileCommand="exclude,java/lang/String.indexOf,(Ljava/lang/String;)I"

    1. 设置编译器跳过编译所有类的 indexOf 方法

    -XX:CompileCommand=exclude,*.indexOf

Java 字符串

Java 9 的 String,引入了类似 Python str 的压缩功能。原理很简单,如果 String 只包含 Latin1 字符,1 字节存一个字符够用了,如果 String 含有中文,那么就换一种编码方式存储,一般一个字符存储两个字节。定义如下:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

    /**
     * The value is used for character storage.
     *
     * @implNote This field is trusted by the VM, and is a subject to
     * constant folding if String instance is constant. Overwriting this
     * field after construction will cause problems.
     *
     * Additionally, it is marked with {@link Stable} to trust the contents
     * of the array. No other facility in JDK provides this functionality (yet).
     * {@link Stable} is safe here, because value is never null.
     */
    @Stable
    private final byte[] value;

    /**
     * The identifier of the encoding used to encode the bytes in
     * {@code value}. The supported values in this implementation are
     *
     * LATIN1
     * UTF16
     *
     * @implNote This field is trusted by the VM, and is a subject to
     * constant folding if String instance is constant. Overwriting this
     * field after construction will cause problems.
     */
    private final byte coder;

    /** Cache the hash code for the string */
    private int hash; // Default to 0
}

final class StringLatin1 {
}

final class StringUTF16 {
}

字符序列存储在字节数组 value 中,然后用一个字节的 coder 表示编码,这是 String 的基本构成。然后 StringLatin1 提供一组静态方法,用来处理只含有 Latin1 字符时的情况。相应的 StringUTF16 提供另一组静态方法,处理包含 Latin1 以外字符时的情况。

注意类的定义,三个类都是 final,且只有 String 有 public 修饰,所以我们作为 JDK 的用户,只能使用 String,而不能使用 StringLatin1 或者 StringUTF16,这两个类不属于 API,属于实现细节,我们既不能使用,也不能依赖其内部实现。但是我们应该理解它,顺从它,避免做出违背它的事情来。

该特性由 +XX:-CompactStrings 提供。

IGV

Overview

The JVM support is controlled by the flag -XX:PrintIdealGraphLevel=# where # is:

  • 0: no output, the default
  • 1: dumps graph after parsing, before matching, and final code (also dumps graphs for failed compilations, if available)
  • 2: more detail, including after loop opts
  • 3: even more detail
  • 4: prints graph after parsing every bytecode (very slow)

为了方便查看字节码编译到本地指令过程中程序的中间表示(IR),使用 JDK 官方的可视化工具 IdealGraph 可以展示每一步优化后的 IR。使用下面命令生成 IGV 数据:

build/linux-aarch64-server-slowdebug/images/jdk/bin/java --add-modules=jdk.incubator.vector -XX:PrintIdealGraphLevel=1 -XX:PrintIdealGraphFile=b2i.xml -XX:CompileCommand=compileonly,TestCastB2XNode.* TestCastB2XNode

新的 instanceOf 操作符

传统上,我们用 Java 写程序判断一个对象是否属于某个类,如果是的话就做 cast 转换会写如下代码:

if (animal instanceof Cat) {
    Cat cat = (Cat) animal;
    cat.meow();
   // other cat operations
} else if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
    dog.woof();
    // other dog operations
}

这样的代码冗长且容易出错。在 Java 14 中引入了一种新的语法解决了这个问题:

if (animal instanceof Cat cat) {
    cat.meow();
} else if(animal instanceof Dog dog) {
    dog.woof();
}

使用 EpsilonGC

在一些性能测试场景中,为了排除 GC 线程的影响,可以使用 EpsilonGC 禁止 GC 操作:

-Xmx16g -Xms16g -XX:+AlwaysPreTouch -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC

注意,这里把堆设了很大,因为在禁止 GC 的条件下,很容易出现out of memory的异常。

文档信息

Search

    Table of Contents