有哪些常用的 JVM 启动参数?
面试考察点
- 基础掌握度:你能不能把参数和 JVM 内存区域对应起来,而不是孤立地记住几个名字。
- 生产实践意识:有没有真实的调优经历,还是只看过书。
- 原理深度:不同 GC 的参数差异、G1 和 ZGC 的调优思路,能聊到这个层次说明功底扎实。
核心答案
常用参数按功能分 5 大类:
| 分类 | 代表参数 | 干嘛用的 |
|---|---|---|
| 内存设置 | -Xms、-Xmx、-Xmn、-Xss | 控制堆、栈大小 |
| 垃圾收集器 | -XX:+UseG1GC、-XX:+UseZGC | 选 GC 算法 |
| GC 日志 | -Xlog:gc*、-XX:+PrintGCDetails | 排查 GC 问题 |
| 元空间 | -XX:MetaspaceSize、-XX:MaxMetaspaceSize | 控制方法区大小 |
| 故障诊断 | -XX:+HeapDumpOnOutOfMemoryError | OOM 时自动 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 = -Xmx、MaxMetaspaceSize 上限、GC 日志、HeapDumpOnOutOfMemoryError。
总结
JVM 参数核心就 5 类:内存、GC 器、GC 日志、元空间、故障诊断。生产环境 4 个必加项——堆大小固定、元空间设上限、开 GC 日志、开 OOM dump。能把参数和实际场景对应起来讲,面试官就知道你是真干过活的。
