Добавлен -XX:MaxHeapFreeRatio=30 -XX:MinHeapFreeRatio=10 в start.sh на проде
и зафиксирован в README с замерами, потому что флаги JVM не переносятся
копированием конфигов — при переезде они теряются молча.
Замер, мир 18 МБ, 0 игроков, 100 с простоя:
коммит живые RSS
без флагов 630 МБ 278 МБ 0.99 ГБ
с флагами 389 МБ 274 МБ 0.69 ГБ
Минус 300 МБ RSS без единого ручного GC.
Причина была не в отсутствии Full GC: jcmd GC.run отрабатывает, живые падают
289 -> 248 МБ, но коммит остаётся на 725 МБ. G1 сжимает кучу только когда
свободно выше MaxHeapFreeRatio, а он по умолчанию 70. При 630 МБ коммита
свободно было 56% — ниже порога, и 350 МБ держались вечно.
Паузы Mixed (то, что ест кадр в рантайме) не ухудшились: median 11.0 -> 9.8,
p95 28.3 -> 28.1, max 38.1 -> 28.7 мс. Concurrent-циклов стало больше
(11 -> 18), общий p95 по всем паузам в одном прогоне вырос 126 -> 270 мс —
это замер без игроков, под нагрузкой флаг ещё предстоит перемерить.
DisableExplicitGC оставлен намеренно: он делает System.gc() no-op, и именно
поэтому освобождение сделано флагом, а не вызовом GC. README теперь
описывает профиль как 15-25 игроков с пиком 34, а не 30-40.
8.0 KiB
ZernMC configs
Тюнинг под профиль: 4 vCPU / 8 GB RAM, 15–25 игроков постоянно, пик 34,
боевой сервер с плагином, который спавнит много Display/Marker сущностей и
частиц (BlackKnife как референс).
Все файлы парциальные — Paper/Spigot дополняет недостающие ключи дефолтами, поэтому их можно копировать поверх существующих конфигов.
Куда класть
| Файл | Путь на сервере |
|---|---|
spigot.yml |
<сервер>/spigot.yml |
paper-world.yml |
<сервер>/<мир>/paper-world.yml — у каждого мира свой |
paper-global.yml |
<сервер>/config/paper-global.yml |
server.properties.patch |
ключи из него в <сервер>/server.properties |
Что где меняем и зачем
spigot.yml
view-distance 8 / simulation-distance 5— на 4 ядрах это самый ощутимый рычаг: симуляция шириной 10 стоит в разы дороже 5.entity-tracking-range.display 128 → 48— главный фикс. Display, ItemDisplay, TextDisplay, Marker шлются игрокам пачками; при спаме боеспособностей 128 это десятки пакетов на тик.misc 96 → 48,other 64 → 32,animals/monsters 96 → 64,players 128 → 96— то же, но по остальным типам.entity-activation-range— сужаем радиусы, в которых сущности вообще тикают;tick-inactive-villagers: false— самый частый выигрыш.ticks-per.hopper-check 1 → 8— хопперы по умолчанию проверяются каждый тик, 8 экономит заметно и не ломает фермы.merge-radius.item 0.5 → 2.5— меньше сущностей-дропа на земле.item-despawn-rate 6000 → 3000,arrow-despawn-rate 1200 → 300.mob-spawn-range 8 → 6,max-tnt-per-tick 100 → 30.
paper-world.yml
misc.redstone-implementation: ALTERNATE_CURRENT— быстрее распространение редстоуна. Ломает 0-tick и двери-контры, предупредить игроков перед включением.tick-rates— рост травы, обновление контейнеров, спавнеры в 3 тика.collisions.max-entity-collisions 8 → 5.environment.disable-thunder,optimize-explosions,max-block-ticks/max-fluid-ticksпополам.hopper.disable-move-event: true— ломает плагины, слушающиеInventoryMoveItemEvent(авто-сортировки, economy-логики).entities.spawning.spawn-limits— потолок мобов на чанк.
paper-global.yml
chunk-system.worker-threads: 2— самый недооценённый рычаг.auto(-1) на 4 ядрах даёт 1 воркер, и генерация чанков идёт последовательно: Moonrise делитavailableProcessorsпополам, а на результате 2 опускается до 1, оставляя место главному треду. Замерено: холодный старт 38.5 → 24.9 с, тёплый 16.4 → 12.2 с. Значение 3 — только с профилем, главному треду на 4 ядрах нужно дышать.chunk-system.io-threads: 2,gen-parallelism: true.chunk-loading.advanced.player-max-concurrent-chunk-loads/generates— защита от флуда генерацией при скоростном облёте. При ускорении генерации этот потолок надо поднимать, иначе движер быстрее, а ограничитель остался прежним.
Что НЕ трогаем
- Moonrise (чанковая система) — уже многопоточный, отдельный патч не нужен.
wake-up-inactive.*— баланс, не перф.
JVM-флаги кучи (живут в start.sh, не в конфигах)
Эти флаги не переносятся копированием конфигов — они в скрипте запуска. Потеряются при переезде, поэтому дублируются здесь.
-XX:MaxHeapFreeRatio=30 -XX:MinHeapFreeRatio=10
Замер на сервере с миром 18 МБ, 0 игроков, после 100 с простоя:
| коммит | живые | RSS | |
|---|---|---|---|
| без флагов | 630 МБ | 278 МБ | 0.99 ГБ |
| с флагами | 389 МБ | 274 МБ | 0.69 ГБ |
Минус 300 МБ RSS на пустом сервере, без единого вызова GC вручную.
Почему работает. G1PeriodicGC гоняет Mixed/Remark каждые 15 с, но кучу
почти никогда не сжимает: G1 сжимает только когда доля свободной памяти
выше MaxHeapFreeRatio, а он по умолчанию 70. При 630 МБ коммита и 278 МБ
живых свободно было 56% — ниже порога, поэтому 350 МБ коммита держались
вечно и в RSS, и в физической памяти.
Отдельно проверено, что принудительный Full GC сам по себе не помогает:
jcmd GC.run отработал, живые упали 289 → 248 МБ, а коммит остался на
725 МБ. То есть дело не в отсутствии Full GC, а именно в пороге.
Паузы при этом не ухудшились — то, что реально ест кадр в рантайме:
| Pause Young (Mixed) | без флагов | с флагами |
|---|---|---|
| median | 11.0 мс | 9.8 мс |
| p95 | 28.3 мс | 28.1 мс |
| max | 38.1 мс | 28.7 мс |
Оговорка: concurrent-циклов стало больше (11 → 18), потому что куча меньше и G1 переполняет её чаще. Каждый такой цикл начинается с STW-паузы, и общий p95 по всем паузам в одном прогоне вырос (126 → 270 мс). Это замер на сервере без игроков, один прогон — под нагрузкой это надо перемерить, прежде чем считать флаг безопасным.
-XX:+DisableExplicitGC
Оставлен намеренно: System.gc() отключён, чтобы ни плагин, ни сторонний код
не могли вызвать stop-the-world сборку в неподходящий момент. Именно поэтому
освобождение кучи сделано флагом, а не вызовом GC.
Что будет, когда мир вырастет до 2–4 ГБ
Флаг этого не ломает. Он разрешает сжимать кучу, когда свободно больше 30%,
и не мешает расти: при -Xmx4G G1 расширится сам, как только живых данных
станет больше. Экономия в 300 МБ в основном на простое и при малом мире.
Порядок применения
- Остановить сервер.
- Скопировать файлы.
paper-world.ymlположить в каждый мир:world/,world_nether/,world_the_end/.- Флаги кучи из раздела выше — в
start.sh, вручную. - Старт → в логе не должно быть
Error occurred while loadingпо этим файлам. - Проверить:
/paper entity(tracking ranges),sparkдля TPS/MSPT.