Skip to content

JVM重要参数总结

堆内存相关

在我们的dockerfile中我们可以设置两个名为Xms和Xmx的参数,这两个参数是用于什么呢?

显式指定堆内存-Xms-Xmx

如果我们要指定最小和最大堆大小(推荐显示指定大小),可以用以下参数:

xml
-Xms<heap size>[unit]//最小
-Xmx<heap size>[unit]//最大
  • heap size 表示要初始化内存的具体大小。
  • unit 表示要初始化内存的单位。单位为 “ g”* (GB)、“ m”*(MB)、*“ k”*(KB)。

例如分配JVM最小2GB,最大5GB的堆内存大小:

xml
-Xms2G -Xmx5G

显示新生代内存(Young Generation)

在堆总可用内存分配完成后,第二大影响因素是Young Generation在堆中所占比例。

一共有两种指定方法:

1.通过-XX:NewSize-XX:MaxNewSize

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

比如

xml
-XX:NewSize=256m
-XX:MaxNewSize=1024m

2.通过-Xmn<young size>[unit]指定

xml
-Xmn256m

TIP:将新对象预留在新生代,由于 Full GC 的成本远高于 Minor GC,因此尽可能将对象分配在新生代是明智的做法,实际项目中根据 GC 日志分析新生代空间大小分配是否合理,适当通过“-Xmn”命令调节新生代大小,最大限度降低新对象直接进入老年代的情况。

此外,还可以通过-XX:NewRatio=<int>设置老年代与新生代的内存比值

比如下面的参数就是设置老年代与新生代内存的比值为 1。也就是说老年代和新生代所占比值为 1:1,新生代占整个堆栈的 1/2。

xml
-XX:NewRatio=1

显示指定永久代/元空间大小

1.永久代PermGen

从Java8开始,如果我们没有指定Metaspace元空间大小,随着更多类创建,理论上JVM会耗尽所有可用系统内存(受到OS和JVM限制),而PermGen永久代则一般是固定大小。

JDK1.8之前永久代还没被彻底移除的时候,通常通过下面这些参数来调节方法区大小:

powershell
-XX:PermSize=N #方法区 (永久代) 初始大小
-XX:MaxPermSize=N #方法区 (永久代) 最大大小,超过这个值将会抛出 OutOfMemoryError 异常:java.lang.OutOfMemoryError: PermGen

GC在这个区域很少,但是并非永久。

2.元空间MetaSpace

JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是本地内存。

powershell
-XX:MetaspaceSize=N #设置 Metaspace 阈值
-XX:MaxMetaspaceSize=N #设置 Metaspace 的最大大小
  • MetaspaceSize容易被误解为初始容量,但事实是对于64位JVM,初始容量都约20.8m
  • 频繁使用-XX:MetaspaceSize扩容到指定参数后就会发生FGC,而后每次扩容都会FGC; 而MetaspaceSize只是表示发生FGC的阈值。

垃圾搜集器内部是根据变量 _capacity_until_GC来判断 Metaspace 区域是否达到阈值的,初始化代码如下所示:

java
void MetaspaceGC::initialize() {
  // Set the high-water mark to MaxMetapaceSize during VM initialization since
  // we can't do a GC during initialization.
  _capacity_until_GC = MaxMetaspaceSize;
}

垃圾收集相关

垃圾回收器

JVM 具有四种类型的 GC 实现:

  • 串行垃圾收集器
  • 并行垃圾收集器
  • CMS 垃圾收集器
  • G1 垃圾收集器

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

ini
-XX:+UseSerialGC
-XX:+UseParallelGC
-XX:+UseConcMarkSweepGC
-XX:+UseG1GC

GC日志

ini
# 必选
# 打印基本 GC 信息
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
# 打印对象分布
-XX:+PrintTenuringDistribution
# 打印堆数据
-XX:+PrintHeapAtGC
# 打印Reference处理信息
# 强引用/弱引用/软引用/虚引用/finalize 相关的方法
-XX:+PrintReferenceGC
# 打印STW时间
-XX:+PrintGCApplicationStoppedTime

# 可选
# 打印safepoint信息,进入 STW 阶段之前,需要要找到一个合适的 safepoint
-XX:+PrintSafepointStatistics
-XX:PrintSafepointStatisticsCount=1

# GC日志输出的文件路径
-Xloggc:/path/to/gc-%t.log
# 开启日志文件分割
-XX:+UseGCLogFileRotation
# 最多分割几个文件,超过之后从头文件开始写
-XX:NumberOfGCLogFiles=14
# 每个文件上限大小,超过就触发分割
-XX:GCLogFileSize=50M

处理OOM

内存不足时,很难通过复制来解决,JVM提供了一些参数:

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

TIP:

  • HeapDumpOnOutOfMemoryError :指示 JVM 在遇到 OutOfMemoryError 错误时将 heap 转储到物理文件中。
  • HeapDumpPath :表示要写入文件的路径; 可以给出任何文件名; 但是,如果 JVM 在名称中找到一个 <pid> 标记,则当前进程的进程 id 将附加到文件名中,并使用.hprof格式
  • OnOutOfMemoryError :用于发出紧急命令,以便在内存不足的情况下执行; 应该在 cmd args 空间中使用适当的命令。例如,如果我们想在内存不足时重启服务器,我们可以设置参数: -XX:OnOutOfMemoryError="shutdown -r"
  • UseGCOverheadLimit :是一种策略,它限制在抛出 OutOfMemory 错误之前在 GC 中花费的 VM 时间的比例

其他

  • -server : 启用“ Server Hotspot VM”; 此参数默认用于 64 位 JVM
  • -XX:+UseStringDeduplication : Java 8u20 引入了这个 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 : 它尽可能优化字符串串联操作