获取不同虚拟机参数的终极方法
在学习和实践虚拟机相关知识的过程中,经常会疑问某个特性在我现在用的版本中启用了么,需要手动配置启用么?当前虚拟机有700项左右的配置参数,不同版本和模式的虚拟机默认值也是不同的,不论是查文档或是网上搜索都很难快速的找到答案,最好的办法就是自己动手寻找答案。
虚拟机类型
在只考虑Oracle的HotSpot虚拟机时,区分不同虚拟机需要考虑大版本(1.6、1.7、1.8)、小版本(u43、u162)、位数(32、64)、模式(server、client),以上任意变化都可能导致默认参数的不同,在验证一些新特性时尽量保证服务器和本地环境一致。
虚拟机模式
虚拟机(HotSpot)有client和server两种模式,分别用 -client
和 -server
参数启用。
$ java -client -version
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) Client VM (build 25.162-b12, mixed mode)
or
$ java -server -version
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) Server VM (build 25.162-b12, mixed mode)
client和server模式一些默认值有所不同,最明显的就是server模式(jdk1.8)默认使用吞吐量优先收集器(UseParallelGC)、默认打开逃逸分析(DoEscapeAnalysis),这些配置让server模式有更好的资源利用以及长期稳定运行支持。
如果没有显式指定 -client
和 -server
参数,虚拟机会根据运行环境是否服务器级别选择是否启用server模式,但是这让人很不放心;同时即使有时候显示指定该参数,由于当前安装版本不支持(例如jdk8u151的64位windows版本仅支持server模式),依然不会生效。如何能确认当前服务器上的虚拟机运行在server模式呢?答案是通过系统属性变量:
System.out.println(System.getProperty("java.vm.name"));
Java HotSpot(TM) Client VM
or
Java HotSpot(TM) 64-Bit Server VM
查看配置的方法
JDK1.6u21后HotSpot虚拟机提供了 -XX:+PrintFlagsFinal
用来查看最终生成的虚拟机配置参数,可以用在启动配置,甚至没有启动类时对 -version
生效(注意在有启动类时不要带 -version
,会阻挡执行后面的启动类)。
java -server -XX:+PrintFlagsFinal -version
它生的结果有5列:
Type | Name | Operator | Value | Application
如:
java -server -XX:+PrintFlagsFinal -version | grep "UseSerialGC\|UseG1GC\|UseParallelGC\|UseConcMarkSweepGC"
bool UseConcMarkSweepGC = false {product}
bool UseG1GC = false {product}
bool UseParallelGC := true {product}
bool UseSerialGC = false {product}
其中需要注意第三列,:=
意味着值是被修改的,=
表示默认值,其他列对使用者意义不大。
配合使用 -XX:+UnlockExperimentalVMOptions
和 -XX:+UnlockDiagnosticVMOptions
可以解锁显示更多隐藏配置参数。
类似命令 -XX:+PrintCommandLineFlags
仅展示第三列为 :=
的参数,即被修改或主动设置过的参数。
类似命令 -XX:+PrintFlagsInitial
仅展示第三列为 =
的参数,即默认值,意义不大。
在使用实践过程中,发现 -client
模式下 PrintFlagsFinal
显示 UseSerialGC = false
,而实际上如果打印GC信息 -XX:+PrintGCDetails
会看到[DefNew: 4416K->512K...]
这样的信息,即实际上该值为true,这是一项历史bug,在jdk9修复(因为默认使用g1收集器)。
universe.cpp:
727 else { // UseSerialGC
728 // Don't assert that UseSerialGC is set here because there are cases
729 // where no GC it set and we then fall back to using SerialGC.
730 status = Universe::create_heap<GenCollectedHeap, MarkSweepPolicy>();
731 }