资讯动态

27 面试官问你 JVM 参数怎么调,他其实想听你怎么定堆大小和选 GC

发布时间:2026/10/14 9:14:40 来源:尧图企业网站定制
面试现场面试官放下手里的笔。面试官简历上写了熟悉 JVM那我问个落地的。线上一个服务GC 有点频繁你上去第一件事干什么候选人A看 GC 日志然后把堆调大加 -Xmx。面试官加到多少这个数字怎么来的候选人A……看机器内存吧能给多少给多少越大越不容易 GC。面试官笑笑没接话转头看候选人B。候选人B我不急着动参数。先去日志里分清是 Minor GC 频繁还是 Full GC 频繁再看每次 GC 之后老年代还剩多少对象——那才是真正活着、必须住下来的东西。堆不是越大越好堆大了单次 Full GC 的停顿也会变长。调参数是收尾的一步难的在判断。面试官那你把这套判断展开讲讲。候选人B接过了话头……你猜谁过了这轮。一、仓库、租约先搞懂参数各管哪一块把 JVM 想成一个仓库。堆就是租来的场地你放进去的对象就是货。参数呢就是租约里的条款——场地多大、哪块区域干什么用、什么时候清理。先把最常用的一批参数摆出来参数管什么说明-Xms初始堆大小建议和-Xmx设成一样-Xmx最大堆大小堆的上限最容易被乱调的一个-Xmn年轻代大小等价于同时设NewSize与MaxNewSize-XX:SurvivorRatioEden 与 Survivor 比例默认 8即 Eden:S0:S1 8:1:1-XX:MetaspaceSize首次触发元空间 GC 的水位注意它不是初始大小-XX:MaxMetaspaceSize元空间上限默认不限制受本地内存约束-Xss单个线程栈大小与平台、位数有关-XX:MaxTenuringThreshold晋升老年代的年龄阈值默认 15CMS 下默认 6这里有个坑很多人在面试里栽过-XX:MetaspaceSize听着像元空间的初始大小其实它是个触发 GC 的水位线。元空间一开始很小用到这个值就会触发一次回收清理不再使用的类只有-XX:MaxMetaspaceSize才是真正的上限。JDK 8 移掉永久代之后类的元数据搬到了元空间用的是本地内存不再算进-Xmx。你在日志里看到元空间相关 GC 却怎么加-Xmx都不管用八成就是栽在这。还有一件事要说清楚如果你不显式指定HotSpot 有一套自带的默认值。最大堆大致是物理内存的四分之一初始堆是六十四分之一在容器里会按容器的内存配额来算。所以你写个 demo 跑起来没配参数也不至于炸但这套默认值拿到生产上就得掂量掂量了。堆的布局和这些参数的落点看图图里能看到堆分成年轻代和老年代年轻代里再分成 Eden 和两块 Survivor。新对象一般生在 Eden一次 Minor GC 活下来的挪到 Survivor来回倒几次、年龄够了才晋升到老年代。-Xmn定的是整个年轻代SurvivorRatio再把年轻代切给 Eden 和 Survivor。二、堆到底定多大别拍脑袋调堆大小说到底是回答两个问题老年代放得下放不下年轻代够不够周转。先说老年代。判据特别朴素——看每次 Full GC 之后老年代还剩多少。这份GC 后存活大小才是真正被长期占用的内存。如果它已经逼近老年代上限Full GC 就会越来越频繁怎么调都是治标不治本得去查是谁在疯狂攒对象缓存没设上限大对象一直往集合里塞。反过来如果 GC 后老年代空荡荡那说明老年代给大了白占内存。再说年轻代。年轻代太小Minor GC 就会很频繁而且 Survivor 太小会让本该多活几轮的对象过早晋升到老年代把 Full GC 的节奏带快。所谓过早晋升premature promotion就是对象还没到年龄就被挤进老年代明明可以下次就死掉的结果跑去老年代占位子。年轻代调大能缓解但也不能无限大——老年代 堆总量 - 年轻代年轻代吃掉太多老年代就不够了。要守的一条原则把-Xms和-Xmx设成一样。两个值不一样时JVM 会在运行中根据负载反复伸缩堆伸缩本身有开销也让内存占用忽高忽低不好监控。生产环境基本都锁死成相等。还有一条堆不是越大越好。堆越大单次 Full GC 需要遍历和整理的对象就越多停顿时间往往更长。低延迟的服务宁愿堆小一点、GC 勤一点也不愿意偶尔来一次长停顿。三、GC 怎么选按延迟和吞吐来取舍选收集器本质是在延迟和吞吐之间做取舍。吞吐是干活时间占多少延迟是单次 GC 停多久。游戏、交易、网关这类服务看延迟离线批处理看吞吐。几个主流选择-Serial单线程收集实现简单适合客户端或小堆。-XX:UseSerialGC-Parallel多线程主打吞吐量JDK 8 服务端默认。-XX:UseParallelGC可配-XX:MaxGCPauseMillis控制停顿目标。-CMS以最短停顿为目标用并发标记清除。它在 JDK 9 被标记为废弃JDK 14 已经被移除新项目别再选它。-G1把堆切成一个个 Region可以按预期的停顿做回收JDK 9 起成为默认收集器。-XX:UseG1GC常用-XX:MaxGCPauseMillis默认目标 200 毫秒、-XX:G1HeapRegionSize、-XX:InitiatingHeapOccupancyPercentIHOP默认 45。-ZGC追求极低停顿JDK 15 起可用于生产。-XX:UseZGC选哪个先看 JDK 版本。如果你手上主流还是 JDK 8那把 Parallel 和 G1 的区别搞明白就够了。JDK 8 默认是 Parallel堆大、对停顿敏感的可以手动切 G1。四、GC 日志怎么看只盯四件事日志开着才有得看。JDK 8 用这套-XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:/path/to/gc.logJDK 9 之后换成了统一日志框架-Xlog:gc*:file/path/to/gc.log:time,uptime,level,tags日志长什么样、每一段分别代表什么看图拿一行的格式来说下面是 Parallel 收集器在 JDK 8 的典型格式12.345: [GC (Allocation Failure) [PSYoungGen: 262144K-34720K(305664K)] 262144K-42160K(1005056K), 0.0234567 secs]-12.345是 JVM 启动后的相对秒数用来算间隔-GC (Allocation Failure)说明这是一次年轻代 GC触发原因是新生代分配不下-PSYoungGen: 262144K-34720K(305664K)是年轻代 GC 前 - GC 后这一代的总容量-262144K-42160K(1005056K)是整个堆 GC 前 - GC 后堆总容量-0.0234567 secs是这次 GC 的暂停时间看日志别一行行抠就盯四件事频率Minor GC 多久一次、Full GC 有没有反复出现、单次耗时停顿是不是在变长、GC 后存活大小真正占内存的对象规模、有没有 Full GC有且频繁就是重点排查对象。再补两个排查用的参数-XX:HeapDumpOnOutOfMemoryError让 JVM 在内存溢出时自动 dump 堆-XX:HeapDumpPath指定 dump 文件路径。出事之后总得留下现场。 面试官真正想听的答案问他 JVM 调优他想听的不是你把参数表背一遍——那些-Xms、-Xmx谁都会念。他想听的是你有没有一套先看后调的判断逻辑。第一层给结论调优不是先调参数是先看现象。我会先分清是 Minor GC 频繁还是 Full GC 频繁再看 GC 后的存活大小判断到底是堆不够、还是对象泄漏、还是年轻代太小。调参数是排在后面的事前面那步是判断。这一句就把你和上来就加 Xmx的人分开了。第二层讲取舍堆大小的下限由 GC 后的存活对象决定年轻代要够周转但不能挤占老年代-Xms与-Xmx锁死相等避免伸缩。收集器的选择看延迟还是吞吐JDK 8 服务端默认 Parallel低延迟考虑 G1 或 ZGCCMS 已经退出历史舞台。第三层给一个可操作的习惯把 GC 日志长期开在文件里出事第一反应是翻日志里的频率、停顿和 GC 后存活量再配上 OOM 自动 dump把现场留住。你能把看什么、看到什么意味着什么讲清楚面试官就知道你是真上过线的人而不是背过题的。下篇预告下一篇换个轻一点的题但坑一点也不少String。面试官特别爱问String 为什么设计成不可变的还有那个经典的和equals的区别。他真正想听的是常量池、intern()还有拼接到底在编译期做了什么手脚。别小看这几个点里面藏着 JDK 从 6 到 9 的好几处大改动。唠点键盘之外的 · 第 27 篇

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑