Files
SashegDev 42f48fea43
Build Delta / Build paperclip jar (push) Successful in 7m3s
perf(config): document and justify the heap-shrink flags
Добавлен -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.
2026-10-04 13:55:14 +00:00
..

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 МБ в основном на простое и при малом мире.

Порядок применения

  1. Остановить сервер.
  2. Скопировать файлы.
  3. paper-world.yml положить в каждый мир: world/, world_nether/, world_the_end/.
  4. Флаги кучи из раздела выше — в start.sh, вручную.
  5. Старт → в логе не должно быть Error occurred while loading по этим файлам.
  6. Проверить: /paper entity (tracking ranges), spark для TPS/MSPT.