有哪些常用的 JVM 启动参数?


面试考察点

  1. 基础掌握度:你能不能把参数和 JVM 内存区域对应起来,而不是孤立地记住几个名字。
  2. 生产实践意识:有没有真实的调优经历,还是只看过书。
  3. 原理深度:不同 GC 的参数差异、G1 和 ZGC 的调优思路,能聊到这个层次说明功底扎实。

核心答案

常用参数按功能分 5 大类:

分类代表参数干嘛用的
内存设置-Xms-Xmx-Xmn-Xss控制堆、栈大小
垃圾收集器-XX:+UseG1GC-XX:+UseZGC选 GC 算法
GC 日志-Xlog:gc*-XX:+PrintGCDetails排查 GC 问题
元空间-XX:MetaspaceSize-XX:MaxMetaspaceSize控制方法区大小
故障诊断-XX:+HeapDumpOnOutOfMemoryErrorOOM 时自动 dump

逐个说。

深度解析

一、内存设置参数

这部分是日常碰到最多的。

# 堆内存设置
-Xms512m                    # 初始堆大小(等同于 -XX:InitialHeapSize)
-Xmx2g                      # 最大堆大小(等同于 -XX:MaxHeapSize)
-Xmn256m                    # 年轻代大小
-Xss512k                    # 每个线程的栈大小(等同于 -XX:ThreadStackSize)

# 元空间设置(JDK 8+)
-XX:MetaspaceSize=256m      # 元空间初始大小
-XX:MaxMetaspaceSize=512m   # 元空间最大大小

# 直接内存
-XX:MaxDirectMemorySize=256m

几个容易踩坑的地方:

  • -Xms-Xmx 建议设成一样。堆动态伸缩会带来性能抖动,扩容要跟 OS 申请内存可能触发 GC,缩容还得整理。生产环境一般设物理内存的 60%~70%,留点给操作系统和元空间。
  • -Xmn 控制年轻代大小,但用 G1 的时候通常不设它,G1 靠 -XX:MaxGCPauseMillis 自己调节更靠谱。
  • -Xss 默认值看平台,Linux 64 位约 1MB,Windows 约 320KB。线程数特别多的场景(几万个)可以适当调小省内存。
  • 元空间用的是本地内存,不在堆里。-XX:MaxMetaspaceSize 不设的话默认没上限,我见过一台机器被元空间吃满的案例。生产环境一定加上限。

二、垃圾收集器参数

# Serial 收集器(单线程,Client 模式默认)
-XX:+UseSerialGC

# Parallel Scavenge + Parallel Old(JDK 8 默认,吞吐量优先)
-XX:+UseParallelGC
-XX:ParallelGCThreads=4              # GC 线程数

# CMS 收集器(JDK 9 废弃,JDK 14 移除,老项目可能还活着)
-XX:+UseConcMarkSweepGC

# G1 收集器(JDK 9+ 默认)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200             # 目标最大停顿时间(默认 200ms)
-XX:G1HeapRegionSize=8m              # Region 大小(1/2/4/8/16/32MB)

# ZGC(JDK 15+ 生产可用)
-XX:+UseZGC
-XX:ZCollectionInterval=0            # GC 间隔,0 表示按需触发
-XX:SoftMaxHeapSize=1g               # 软上限,尽量不超这个值

选型思路一句话版:JDK 8 默认 Parallel GC 吞吐高但停顿长,适合离线批处理;JDK 9+ 默认 G1 是通用首选;ZGC 给延迟敏感的场景用(金融交易之类的),JDK 17 之后已经很成熟。CMS?移除了,别碰。

这块面试官特别爱追问你线上用的什么 GC、为什么选它。很多人到这就卡了。原因一般是只背了参数没实际选过,或者压根不知道自己项目用的哪个。

三、GC 日志参数

# JDK 8 及之前
-XX:+PrintGCDetails                   # 打印详细 GC 日志
-XX:+PrintGCDateStamps                # 打印时间戳
-XX:+PrintGCTimeStamps                # 打印 JVM 启动后的相对时间
-XX:+PrintGCApplicationStoppedTime    # 打印 STW 停顿时间
-Xloggc:/var/log/gc.log               # GC 日志输出文件

# JDK 9+ 统一用 -Xlog
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags

JDK 9 把 GC 日志参数统一到了 -Xlog,以前那一堆 PrintXXX 终于不用记了。

生产环境一定要开 GC 日志。我有次排查线上频繁 Full GC,就是靠日志发现是大对象直接进老年代。不开日志的话,出问题基本两眼一抹黑。

四、故障诊断参数

# OOM 时自动生成堆 dump
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heapdump.hprof

# OOM 前执行脚本(发告警、清缓存之类的)
-XX:OnOutOfMemoryError="send_alert.sh"

-XX:+HeapDumpOnOutOfMemoryError 生产环境必加。OOM 的时候你光知道"内存爆了"没用,得知道是哪个对象吃掉的。堆 dump 能帮你定位到具体的类。有一点要注意:dump 文件大小跟堆差不多,磁盘空间得留够。

五、其他常用参数

# 压缩指针(堆 < 32G 默认开启)
-XX:+UseCompressedOops
-XX:+UseCompressedClassPointers

# JIT 编译
-XX:CompileThreshold=10000            # 方法调用多少次后触发 JIT 编译
-XX:+TieredCompilation                # 分层编译(JDK 8 默认开启)

# 系统属性
-Dproperty=value                      # System.getProperty() 能取到

# 远程调试
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

-D 用得特别多。Spring Boot 的配置外部化就靠它,比如 -Dspring.profiles.active=prod

面试高频追问

1. -Xms-Xmx 为什么建议设成一样?

堆扩容要跟 OS 申请内存、可能触发 GC;缩容要整理内存。设成一样,JVM 就不用在这上面来回折腾了。

2. 线上服务 OOM 了怎么排查?

看有没有开 HeapDumpOnOutOfMemoryError,开了就拿 dump 文件分析。没开的话看 GC 日志和监控定位异常时段,进程还活着就用 jmap 手动 dump。

3. G1 和 ZGC 怎么选?

G1 停顿几十到几百毫秒,通用场景够用。ZGC 停顿亚毫秒级,延迟敏感的场景选它。JDK 17+ 如果延迟要求不苛刻,G1 就行。

4. MaxGCPauseMillis 设太小会怎样?

G1 会拼命缩小单次 GC 范围来满足目标,GC 频率暴涨,吞吐量反而掉下来。这个值得根据业务实际能接受的延迟来设,不是越小越好。

常见面试变体

  • "JVM 调优你做过吗?调了哪些参数?"
  • "生产环境 JVM 参数大概长什么样?"
  • "线上 GC 问题怎么排查?"
  • "G1 收集器核心调优参数有哪些?"

记忆口诀

参数分类:内存(ms/mx/mn/ss)、GC 器(Serial/Parallel/G1/ZGC)、GC 日志、诊断 dump,外加 -D 系统属性。

生产必加-Xms = -XmxMaxMetaspaceSize 上限、GC 日志、HeapDumpOnOutOfMemoryError

总结

JVM 参数核心就 5 类:内存、GC 器、GC 日志、元空间、故障诊断。生产环境 4 个必加项——堆大小固定、元空间设上限、开 GC 日志、开 OOM dump。能把参数和实际场景对应起来讲,面试官就知道你是真干过活的。