运行时选项
纯解释执行
-Xint
PrintCompilation
选项
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 用例可以使用如下命令:
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"
ormake 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
举例:
- 设置编译器跳过编译 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
- 设置编译器只跳过编译 java.lang.String 类 int indexOf(String) 方法:
-XX:CompileCommand="exclude,java/lang/String.indexOf,(Ljava/lang/String;)I"
- 设置编译器跳过编译所有类的 indexOf 方法
-XX:CompileCommand=exclude,*.indexOf
- 设置编译器跳过编译 com.jvmpocket.Dummy 类 test 方法的 4 种写法:
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
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
的异常。
文档信息
- 本文作者:Zhuojun Miao
- 本文链接:https://miaozhuojun.github.io/wiki/jvm/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)