原文出处:Aikar(Daniel Ennis),JVM Tuning: Optimized G1GC for Minecraft,发表于 aikar.co,长期运营 Empire Minecraft 生存服的实战沉淀。本文对其核心思想做系统化梳理与解读,并标注引用来源。
一、问题从哪来:为什么默认 JVM 参数跑 MC 会卡?
Minecraft 服务端是一个极其特殊的 Java workload:
分配速率惊人——一个 30 人左右的服务器,内存分配速率可达 至少 800MB/s,且绝大多数对象是极短命的(如
BlockPosition这类临时对象)默认 G1GC 的 New Generation 太小(默认只给堆的 ~5%),导致 Young GC 触发过频,对象被过早晋升(promote)到 Old Gen
Old Gen 一旦涨起来就会触发 Full GC / Mixed GC 的大暂停,玩家感受到的就是那次经典的 TPS 骤降 → 所有人瞬移一下
Aikar 做的事,本质上就是一句话:
让短命对象在 Young Gen 死掉,别让它们漏进 Old Gen;同时把 GC 暂停压扁、摊平,消除尖峰。
二、推荐启动参数(基准版)
下面就是那套被称为 Aikar's Flags 的推荐 JVM 启动参数。只改 Xms/Xmx和你自己的 jar 名,其余原样照搬:
java -Xms10G -Xmx10G \
-XX:+UseG1GC \
-XX:+ParallelRefProcEnabled \
-XX:MaxGCPauseMillis=200 \
-XX:+UnlockExperimentalVMOptions \
-XX:+DisableExplicitGC \
-XX:+AlwaysPreTouch \
-XX:G1NewSizePercent=30 \
-XX:G1MaxNewSizePercent=40 \
-XX:G1HeapRegionSize=8M \
-XX:G1ReservePercent=20 \
-XX:G1HeapWastePercent=5 \
-XX:G1MixedGCCountTarget=4 \
-XX:InitiatingHeapOccupancyPercent=15 \
-XX:G1MixedGCLiveThresholdPercent=90 \
-XX:G1RSetUpdatingPauseTimePercent=5 \
-XX:SurvivorRatio=32 \
-XX:+PerfDisableSharedMem \
-XX:MaxTenuringThreshold=1 \
-Dusing.aikars.flags=https://mcflags.emc.gs \
-Daikars.new.flags=true \
-jar paper.jar --nogui⚠️ 先说最重要的警告:如果你的面板/主机说你有 8000M,请 不要设 Xmx=8000M。Java 在
-Xmx之外还需要额外 native 内存,建议扣掉约 1000–1500M,或者跟主机确认他们是否已帮你兜底。例如 8G 机器通常设 6500M~7000M 更安全。
三、逐组参数:它们在「物理层」到底做了什么?
1. -Xms= -Xmx,且为什么必须相等
Aikar 的核心论点:
如果
-Xms < -Xmx,意味着你给了 Java 一个"可以膨胀到上限但实际没预留"的承诺——闲置内存就是浪费内存G1 在有更多堆可用时,会更从容地让短命对象留在 Young 区自然死亡,而不是被迫提前晋升
+AlwaysPreTouch配合相等的 Xms/Xmx,让内存在进程启动时就被真正分配并连续化,避免在运行时才缺页申请,从而提升访问效率
2. 把 New Gen 拉大到 30%~40% —— 整套 flags 的灵魂
这是最关键的一组:
-XX:G1NewSizePercent=30
-XX:G1MaxNewSizePercent=40G1 默认 New Gen 只占 5% 堆,而 MC 的分配速率会把 Eden 瞬间打满——Young GC 每秒可能触发 1~2 次以上,不但暂停频繁,还会把大量临时对象冲进 Old Gen。
Aikar 把 New Gen 的下限拉到 30%,上限 40%,等于告诉 G1:"给新生代更多空间,让它慢点满,让那些 BlockPosition 级别的短命对象有机会在 Eden/Survivor 里直接被回收。"
效果:Young GC 间隔变长、每次更有成效,Old Gen 增速显著放缓。
3. MaxTenuringThreshold=1+ SurvivorRatio=32
-XX:MaxTenuringThreshold=1
-XX:SurvivorRatio=32MC 的绝大多数分配活不过两轮 GC。Aikar 的做法是:
最多让对象在 Survivor 里熬 1 轮,第 2 轮还活着就直接当"较长寿命"处理,进入 Old 区的 Mixed GC 路径去回收——而不是在 Survivor 之间来回拷贝 15 次
既然 Survivor 的使用被压缩了,就用
SurvivorRatio=32把省出来的 region 还给 Eden,让 Eden 更大
这是一个非常"懂 MC 对象生命周期"的设计,不是通用 Java 应用的套路。
4. IHOP=15早启动并发标记 —— 防 Full GC 于未然
-XX:InitiatingHeapOccupancyPercent=15默认 IHOP 通常在 45% 左右才开始考虑回收 Old Gen。Aikar 把它压到 15%,核心目的:在 Old Gen 还没涨到危险水位之前,就让 G1 开始并发标记周期,用一系列轻量的 Mixed GC 逐步清理,而不是等爆了再全停
配合:
-XX:G1MixedGCLiveThresholdPercent=90
-XX:G1MixedGCCountTarget=4让 Mixed GC 更积极地回收 Old 中可回收区域,同时保持每次暂停可控。
5. Region Size 锁定为 8M —— 防止 Humongous 对象捣乱
-XX:G1HeapRegionSize=8MMC 1.15+ 的某些数据结构(尤其是与区块/实体相关的分配路径)会产生接近 4MB 的对象。G1 的默认 Region Size 在较小堆下可能算出 1M~4M,于是 ≥4MB 的分配会被标记为 Humongous,直接进 Old Gen,且极难回收。手动锁到 8M 就是堵这个坑。
6. 其余"保护性/稳定性"项速览
四、如果你有超过 12G 堆:需要微调的进阶版
Aikar 给出的建议是:≤12G 用基准版不动;>12G 时做如下调整:
# 替换基准版中的对应项:
-XX:G1NewSizePercent=40
-XX:G1MaxNewSizePercent=50
-XX:G1HeapRegionSize=16M
-XX:G1ReservePercent=15
-XX:InitiatingHeapOccupancyPercent=20理由:堆大了,"to-space 不够"的风险降低,可以把更多给 New Gen(40/50),Region Size 升到 16M 减少 Humongous 碎片并加快 Remark,IHOP 延后到 20 因为 Old 绝对值更大了。但他也明确提醒:如果观察到 Old Gen 回收反而变差,回退到基准版。
五、如何验证它真的在工作?
① 启用 GC 日志(滚动,几乎零开销)
Java 11+:
-Xlog:gc*:logs/gc.log:time,uptime:filecount=5,filesize=1MJava 8(老环境):
-Xloggc:gc.log -verbose:gc -XX:+PrintGCDetails \
-XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps \
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=1M日志只有约 5MB,可以随时拿 gc.log跟 TPS 下跌时间点做关联分析。
② 真正的性能诊断顺序
Aikar 自己也说得很直白(以及 Paper 文档的共识):
GC 调优通常是最后一段路。 如果你 TPS 不稳,先看:
/timings或 Spark profiler → 找出是谁在吃 tick 时间实体堆叠(农场/刷怪)、区块加载、红stone、漏斗链、水流推送
插件逐个二分法排查
最后才是 GC 参数压榨剩余抖动
Flags 能让"本来就不该卡的"更平滑,但救不了设计层面的卡。
六、今日使用须知(写给 2025/2026 的服主)
七、总结一句话
Aikar's Flags 不是玄学,是一份针对 Minecraft 极端分配模式量身定制的 G1GC 配置档案。 它的核心价值在于:把 New Gen 撑大、把晋升压住、把回收做成分段增量、把外因(显式 GC / mmap 写入 / 迟分配)封掉——最终换来的是 TPS 曲线从锯齿变成平缓。
如果你愿意,我可以下一步帮你做两件事之一:
按你的具体环境生成一份安全的
start.sh(告诉我:MC 版本、Paper 还是 Purpur、Java 版本号、java -version输出、面板给的总内存、平均在线人数)教你读 GC 日志:把一段
gc.log里的 Full GC / Pause / Humongous 信号翻译成"该怎么调"的结论
引用出处
Aikar, JVM Tuning: Optimized G1GC for Minecraft, aikar.co, 2018-07-02 — 原始 flags 发布页与内存警告
PaperMC Docs, Aikar's Flags, docs.papermc.io — 官方继承版说明与技术解释(Xms=Xmx 原理、NewGen 为何给大、GC 日志)
Aikar, Technical Explanation of the Flags(同文展开段)— 逐项动机:NewSizePercent/IHOP/SurvivorRatio/AlwaysPreTouch/DisableExplicitGC/PerfDisableSharedMem 等
mcflags.emc.gs(Aikar 的 flags 镜像页)— to-space exhaustion、MaxTenuringThreshold=1 意图、G1HeapRegionSize 与 Humongous 问题详解