最重要的JVM参数指南

概述

在这个快速教程中,我们将探索可用于配置 Java 虚拟机的最知名的选项。

堆内存 -Xms 和 -Xmx

最常见的与性能相关的实践之一是根据应用程序需求初始化堆内存。

这就是为什么我们应该指定最小和最大的堆大小:

1
2
-Xms<heap size>[unit]
-Xmx<heap size>[unit]

在这里,unit 表示要初始化内存(用堆大小表示)的单元。单位可以标记为“ g”为 GB,“ m”为 MB,“ k”为 KB。
例如,如果我们想给 JVM 分配最小的 2gb 和最大的 5gb,我们需要写:

1
-Xms2G -Xmx5G

从 java8 开始,Metaspace 的大小没有定义。一旦它达到全局限制,JVM 会自动增加它,然而,为了克服任何不必要的不稳定性,我们可以设置 Metaspace 大小为:

1
-XX:MaxMetaspaceSize=<metaspace size>[unit]

在这里,元/空间大小表示我们要分配给元/空间的内存量。
根据 Oracle 的指导方针,除了总的可用内存之外,第二大影响因素是为年轻一代保留的堆的比例。默认情况下,YG 的最小大小为 1310 MB,最大大小不受限制。
我们可以明确地分配它们:

1
2
-XX:NewSize=<young size>[unit]
-XX:MaxNewSize=<young size>[unit]

垃圾收集

为了提高应用程序的稳定性,选择正确的垃圾收集算法至关重要。

有四种类型的 GC 实现:

  • Serial Garbage Collector 串行垃圾收集器
  • Parallel Garbage Collector 并行垃圾收集器
  • CMS Garbage Collector CMS 垃圾收集器
  • G1 Garbage Collector G1 垃圾收集器
  • Z Garbage Collector ZGC 垃圾收集器

这些实现可以使用以下参数声明:

1
2
3
4
5
-XX:+UseSerialGC
-XX:+UseParallelGC
-XX:+UseParNewGC (会自动启用-XX:+UseConcMarkSweepGC)
-XX:+UseG1GC
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC (java11开始支持,java15之后不再需要-XX:+UnlockExperimentalVMOptions)

有关垃圾收集实现的更多详细信息可以在这里找到。

GC Logging

为了严格监视应用程序的运行状况,我们应该始终检查 JVM 的垃圾收集性能。最简单的方法是以人类可读的格式记录 GC 活动。

使用以下参数,我们可以记录 GC 活动:

1
2
3
4
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=< number of log files >
-XX:GCLogFileSize=< file size >[ unit ]
-Xloggc:/path/to/gc.log

UseGCLogFileRotation 指定日志文件滚动策略,很像 log4j、 s4lj 等。NumberOfGCLogFiles 表示可以为单个应用程序生命周期编写的日志文件的最大数量。GCLogFileSize 指定文件的最大大小。最后,loggc 表示它的位置。

这里需要注意的是,还有两个可用的 JVM 参数(-XX:+PrintGCTimeStamps 和 -XX:+PrintGCDateStamps)可用于在 GC 日志中打印日期时间戳。

例如,如果我们希望分配最多 100 个 GC 日志文件,每个文件的最大大小为 50 MB,并希望将它们存储在‘/home/user/log/’位置,我们可以使用以下语法:

1
2
3
4
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=50M
-Xloggc:/home/user/log/gc.log

但是,问题是总是在后台使用一个附加的守护进程线程来监视系统时间。这种行为可能会造成一些性能瓶颈; 这就是为什么在生产中最好不要使用这个参数的原因。

处理 OOM

对于大型应用程序来说,面对内存不足错误是非常常见的,这反过来会导致应用程序崩溃。这是一个非常关键的场景,很难通过复制来解决这个问题。

这就是为什么 JVM 提供了一些参数,这些参数将堆内存转储到一个物理文件中,以后可以用来查找泄漏:

1
2
3
4
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./java_pid<pid>.hprof
-XX:OnOutOfMemoryError="< cmd args >;< cmd args >"
-XX:+UseGCOverheadLimit

这里有几点需要注意:

  • HeapDumpOnOutOfMemoryError 指示 JVM 在遇到 OutOfMemoryError 错误时将 heap 转储到物理文件中
  • HeapDumpPath 表示要写入文件的路径; 可以给出任何文件名; 但是,如果 JVM 在名称中找到一个 <pid> 标记,则当前进程的进程 id 将附加到文件名中,并使用.hprof格式
    OnOutOfMemoryError is used to issue emergency commands to be executed in case of out of memory error; proper command should be used in the space of cmd args. For example, if we want to restart the server as soon as out of memory occur, we can set the parameter:
  • OnOutOfMemoryError 用于发出紧急命令,以便在内存不足的情况下执行; 应该在 cmd args 空间中使用适当的命令。例如,如果我们想在内存不足时重启服务器,我们可以设置参数:
1
-XX:OnOutOfMemoryError="shutdown -r"
  • UseGCOverheadLimit 是一种策略,它限制在抛出 OutOfMemory 错误之前在 GC 中花费的 VM 时间的比例

32/64 位

在同时安装了 32 位和 64 位包的操作系统环境中,JVM 会自动选择 32 位环境包。

如果我们想手动设置环境为 64 位,我们可以使用下面的参数:

1
-d<OS bit>

操作系统位可以是 32 或 64。更多信息可以在这里找到。

杂项

  • server: 启用“ Server Hotspot VM”; 此参数默认用于 64 位 JVM
  • -XX:+UseStringDeduplication: java8u20 引入了这个 JVM 参数,通过创建太多相同 String 的实例来减少不必要的内存使用; 这通过将重复 String 值减少为单个全局 char []数组来优化堆内存
  • -XX:+UseLWPSynchronization: 设置基于 LWP (轻量级进程)的同步策略,而不是基于线程的同步
  • -XX:LargePageSizeInBytes: 设置用于 Java 堆的较大页面大小; 它采用 GB/MB/KB 的参数; 页面大小越大,我们可以更好地利用虚拟内存硬件资源; 然而,这可能会导致 PermGen 的空间大小更大,这反过来又会迫使 Java 堆空间的大小减小
  • -XX:MaxHeapFreeRatio: 设置 GC 后, 堆空闲的最大百分比,以避免收缩。
  • -XX:SurvivorRatio: eden/survivor 空间的比例, 例如-XX:SurvivorRatio=6 设置每个 survivor 和 eden 之间的比例为 1:6,
  • -XX:+UseLargePages: 如果系统支持,则使用大页面内存; 请注意,如果使用这个 JVM 参数,OpenJDK 7 可能会崩溃
  • -XX:+UseStringCache: 启用 String 池中可用的常用分配字符串的缓存
  • -XX:+UseCompressedStrings: 对 String 对象使用 byte []类型,该类型可以用纯 ASCII 格式表示
  • -XX:+OptimizeStringConcat: 它尽可能优化字符串串联操作

总结

我们了解了一些重要的 JVM 参数,这些参数可用于调优和提高通用应用程序性能。

其中一些还可以用于调试目的。

如果您想更详细地研究参考参数,可以从这里开始。

相关链接

Guide to the Most Important JVM Parameters
JVM Garbage Collectors
Oracle Java HotSpot VM Options
Oracle HotSpot FAQ