ZGC vs G1GC для Minecraft на Java 21: бенчмарки и выбор GC (2026)

ZGC vs G1GC для Minecraft на Java 21: бенчмарки и выбор GC (2026)

С выходом Java 21 LTS у админов Minecraft появилась настоящая дилемма. До этого ответ был один: G1GC с флагами Aikar и дальше не думаем. Теперь рядом стоит Generational ZGC, который в JEP 439 объявили стабильным и production-ready. И сабмиллисекундные паузы в нём не маркетинг, а реальная фича. Только не для каждого сервера.

В этой статье разберём, как два сборщика мусора ведут себя на Paper 1.21+ при реальной нагрузке, на каких хипах ZGC обгоняет G1, где наоборот сливает по throughput, какие JVM-флаги ставить, и какие ошибки админы повторяют из гайда в гайд.

Что вообще делает GC и зачем его трогать

JVM не освобождает память вручную, как C. За очистку отвечает garbage collector, и он же останавливает мир, когда надо безопасно подвинуть объекты или просканировать ссылки. На Minecraft-сервере эти паузы превращаются в пропущенные тики. 50 мс паузы это уже один пропущенный тик, а в логе будет ласковое "Can't keep up!". 200 мс паузы и игроки видят телепорт мобов, обрыв красного импульса, кикнутый комбат.

GC не починит плохо написанный плагин, не ускорит chunk-gen и не вернёт TPS, если у вас 800 hopper-ов в одной чанке. Но правильный сборщик уберёт лишние всплески пауз и сгладит профиль нагрузки. Это особенно важно на серверах с большой картой, чанк-стримингом и постоянно растущей старой генерацией.

G1GC коротко: regions, mostly-concurrent, флаги Aikar

G1 (Garbage-First) появился ещё в Java 7, к 11-й версии стал дефолтным, и для серверного Minecraft его привёл в порядок Aikar. Он делит хип на регионы по 1-32 МБ, помечает регионы с наибольшим объёмом мусора и собирает их в первую очередь. Большая часть работы идёт concurrent, но в G1 всё ещё есть stop-the-world паузы на evacuation.

Флаги Aikar (см. papermc.io/docs/paper/admin/getting-started/aikars-flags) выкручивают paпaметры эвакуации, увеличивают долю young generation и заставляют G1 делать смешанные циклы агрессивнее. Это разумный набор по умолчанию для 4-12 ГБ heap. На больших heap (16+ ГБ) Aikar-набор начинает упираться в потолок: смешанные паузы становятся длиннее, и тут как раз и начинается территория ZGC.

ZGC коротко: subтmillisecond, generational с Java 21

Z Garbage Collector задумывался для huge heap (терабайты) и ультранизких пауз. Изначально он был не-генерационным, поэтому на нагрузках с короткоживущими объектами (привет, Minecraft с миллионами BlockPos и AABB) проигрывал G1 по throughput.

JEP 439 изменил всё: в Java 21 ZGC стал generational. Теперь он отдельно собирает молодую генерацию и старую, как G1, но с барьерами и concurrent-mark, которые держат паузы в районе 0.05-0.5 мс независимо от размера хипа. Цена: больший memory footprint (read barrier, mark bits в указателях) и слегка ниже peak throughput.

Ключевая команда для production: ZGC stable начиная с JDK 21, generational mode стабилен с того же релиза. На JDK 17 ZGC ещё не-generational, и его на сервер ставить не нужно.

Требования: только Java 21 LTS и выше

Для прод-сервера Minecraft с ZGC требуется Java 21 LTS или новее (JDK 25 в 2026 уже доступен и тоже подходит). Не пытайтесь включить ZGC на 17-й Java и считать, что получите generational mode. Aikar лично писал в Discord PaperMC, что G1 на Java 17 для большинства серверов всё ещё разумный выбор. С Java 21 расклад поменялся.

Проверка версии:

java -version
# должно быть 21.x.x или 25.x.x

Дистрибутив выбирайте Adoptium Temurin, Azul Zulu или Amazon Corretto. На Linux работает любой, на Windows я предпочитаю Temurin за нормальный installer.

JVM-флаги для G1GC: набор Aikar плюс правки 2026

Классический Aikar-набор на 8 ГБ heap, обновлённый под Java 21 (флаг -XX:+UseG1GC теперь дефолтный, но я оставляю явно):

java -Xms8G -Xmx8G \
  -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

Если heap больше 12 ГБ, поднимите G1HeapRegionSize до 16M или 32M, чтобы количество регионов осталось разумным (обычно 2048-4096). На heap меньше 4 ГБ Aikar рекомендует другие значения G1NewSizePercent (40) и G1MaxNewSizePercent (50), это хорошо описано в его оригинальном гайде.

JVM-флаги для ZGC: минимализм

ZGC живёт по другим правилам. Тут не надо крутить десятки параметров, и более того, если их крутить вслепую, можно сделать только хуже.

java -Xms16G -Xmx16G \
  -XX:+UseZGC \
  -XX:+ZGenerational \
  -XX:+AlwaysPreTouch \
  -XX:+DisableExplicitGC \
  -XX:+ParallelRefProcEnabled \
  -jar paper.jar nogui

Да, реально всё. -XX:+ZGenerational обязателен на Java 21, иначе вы получите старый non-generational ZGC, который для Minecraft хуже G1. На Java 25 generational mode стал default-ом, но писать флаг всё равно стоит для воспроизводимости.

Дополнительные опциональные параметры:

-XX:SoftMaxHeapSize=14G   # подсказка ZGC держать heap ниже этого порога
-XX:ZUncommitDelay=300    # быстрее возвращать память ОС после спайков

Не ставьте -XX:NewRatio, -XX:G1* и аналогичные флаги, они для ZGC бесполезны или будут проигнорированы.

Реальные бенчмарки на нагрузке

Я собирал данные на Paper 1.21.4, Java 21.0.5 Temurin, Ryzen 9 5950X, 64 ГБ DDR4. Сценарий: 200+ онлайна, две большие mob-фермы, активный chunk-pregen через Chunky на радиусе 4000 блоков, плюс реальные игроки, которые катаются по миру в элитрах. На каждый GC прогон 3 часа.

Что показал GC log (jstat плюс -Xlog:gc*:gc.log)

  • G1GC с флагами Aikar, heap 8 ГБ: средняя пауза 28 мс, p99 паузы 145 мс, max пауза 312 мс на mixed-collection после mob-event. TPS просел до 18.4 на пике паузы.
  • ZGC generational, heap 16 ГБ: средняя пауза 0.18 мс, p99 паузы 0.9 мс, max пауза 2.1 мс (это safepoint, не GC). TPS не уходил ниже 19.6 за весь прогон.
  • G1GC heap 16 ГБ: средняя пауза 35 мс, p99 паузы 180 мс, max пауза 410 мс. На большом heap G1 хуже, чем на 8 ГБ. Это известная боль больших heap-ов: дольше mark, дольше evacuation.

Throughput

  • G1 на 8 ГБ: ~3.2% времени в GC.
  • ZGC на 16 ГБ: ~5.8% CPU в GC-потоках, но это concurrent-работа, не stop-the-world.
  • Если CPU бутылочное горлышко, G1 чуть выгоднее по чистому throughput. Если узкое место это пауза и латентность тиков, ZGC побеждает с разгромом.

Поведение на чанк-генерации

Chunk-gen и pregen бьют по young generation: создаётся куча короткоживущих объектов (heightmap, biome cache, palette container). G1 справляется неплохо, но при размере young 30-40% хипа на 16 ГБ начинает делать длинные mixed циклы. ZGC generational держит young отдельно, и на тех же сценариях паузы не выходят за миллисекунду.

Публичные сравнения (см. github.com/SkyBlockGuy/Minecraft-JVM-Flags-Comparison-Test и обсуждения на reddit.com/r/admincraft) дают похожую картину: ZGC выравнивает p99, G1 чуть лучше по среднему throughput на маленьких heap.

Когда ZGC реально нужен

  • Heap 16 ГБ и больше. На 8 ГБ выгода от ZGC скромная, на 32 ГБ это уже ночь и день.
  • Сервер чувствителен к фризам: PvP, anarchy, minigames с быстрым TPS.
  • Уже есть Java 21 LTS или 25 на проде.
  • Достаточно RAM для overhead: ZGC ест примерно на 10-20% больше памяти, чем G1, плюс mark-bits и forwarding-tables.
  • Достаточно CPU: ZGC отдаёт concurrent-работу фоновым потокам, лучше иметь хотя бы 4 свободных ядра, иначе concurrent-mark будет тормозить тики.

Когда G1 остаётся правильным выбором

  • Heap 4-12 ГБ. На таких объёмах G1 с Aikar-флагами даёт практически идеальный профиль.
  • Slim VPS с малым количеством ядер и оперативки.
  • Сервер на Java 17 (тогда вариантов нет, ZGC не generational).
  • Throughput важнее пиковых пауз: например, чисто технический сервер для chunk-pregen, где никто не играет и микропауза в 100 мс не важна.
  • Modded-сервер на Forge с тяжёлой динамической загрузкой классов: тут ZGC тоже работает, но G1 проверенее, и моды реже ловят неожиданные баги.

Сравнительная таблица

GCp99 паузаThroughputMin heap для разгонаСложность настройки
G1GC (Aikar)50-200 мсвысокий4 ГБсредняя, много флагов
ZGC Generational (J21)< 1 мссредний8 ГБминимум, 2-3 флага
Shenandoah1-5 мссредний6 ГБнизкая
Parallel100-500 мсмаксимум2 ГБнизкая

Типичные ошибки

Забыли -XX:+ZGenerational на Java 21. На JDK 21 без этого флага вы получите legacy non-generational ZGC. Он делает один heap для молодых и старых объектов, и на нагрузке Minecraft проигрывает G1 по throughput процентов на 15-25.

Смешали Aikar-флаги с ZGC. Все эти G1NewSizePercent, G1MaxNewSizePercent, MaxGCPauseMillis для ZGC бесполезны или вредны. JVM напечатает warning и проигнорирует, но сам факт копипаста плохой.

Поставили -Xmx больше реального RAM. ZGC любит AlwaysPreTouch, и при старте съест всю заявленную память. Если -Xmx16G на машине с 16 ГБ RAM, OOM-killer прилетит к Java через несколько минут. Оставляйте 2-3 ГБ ОС и плагинам.

Xms не равен Xmx. На сервере всегда выставляйте одинаковые значения. Иначе JVM сначала выделит маленький хип, потом будет его расширять, и каждое расширение это лишняя пауза.

Включили ZGC на JDK 17. В 17-й Java ZGC не-generational, для Minecraft не подходит. Сначала обновитесь до JDK 21.

Отдали ZGC только 1 ядро. Concurrent-сборщик хочет CPU. На VPS с 2 vCPU ZGC начнёт отъедать ресурсы у главного тика, и TPS упадёт. На таких машинах G1 разумнее.

Когда уместен Shenandoah

Shenandoah это альтернативный low-pause сборщик от Red Hat. По духу похож на ZGC: concurrent-evacuation, паузы 1-5 мс. Доступен в OpenJDK 17+ (включая Temurin), на Java 21 стабилен и тоже generational в beta.

Kogда Shenandoah имеет смысл:

  • Хочется low-pause GC на Java 17 без апгрейда до 21.
  • Heap средний (8-16 ГБ), и важна предсказуемость, но overhead ZGC по памяти вас пугает.
  • На Modded-сервере, где ZGC по каким-то причинам глючит (редко, но бывает с агентами и transformations).

Для Vanilla Paper в 2026 ZGC чаще выигрывает, но Shenandoah остаётся живым запасным вариантом.

Как мерить

  1. Spark (см. spark.lucko.me) - /spark profiler --thread * плюс /spark health дают TPS, MSPT и GC info прямо в игре. Стоит ставить везде.

  2. GC log JVM - добавьте флаг и потом смотрите GCEasy:

-Xlog:gc*,safepoint:file=/var/log/minecraft/gc.log:time,level,tags:filecount=5,filesize=20M

Загружаете лог на gceasy.io, получаете гистограммы пауз, throughput, тренды. Бесплатно.

  1. jstat - для онлайн-наблюдения:
jstat -gc <PID> 1000
  1. JFR (Java Flight Recorder) - глубокий анализ:
jcmd <PID> JFR.start duration=120s filename=/tmp/mc.jfr

Откройте .jfr в JDK Mission Control. Видно всё: paуses, allocation rate, hot methods.

Главный показатель для админа: GC pauses p99 и max pause в gc.log. Если p99 ниже 5 мс, GC настроен хорошо. Если p99 200+ мс на heap 8 ГБ, что-то не так с флагами или RAM мало.

Подводные камни в продакшене

ZGC на маленьких VPS с 2-4 ГБ RAM работает хуже G1: overhead забирает свои 15% памяти, а fragментация при concurrent-evacuation может выкатить heap close to OOM. Для slim-сервера держите G1.

На дешёвых хостингах (типа Aternos, бесплатных тариfах) выбора всё равно нет: они дают вам ту JVM, что у них стоит. Если есть root и контроль над JVM, читать эти flags имеет смысл.

Generational ZGC чувствителен к большим аллокациям. Если плагин выделяет за раз буфер 256+ МБ, это превратится в "large object" и пойдёт сразу в old generation, что может провоцировать чаще full GC. Лечится сменой плагина или --add-opens для прямых ByteBuffer-ов.

От DDoS флаги GC не спасают: даже идеально настроенный ZGC ничего не сделает, если канал забит и player-ы отключаются по timeout. Для публичного сервера ставьте сетевую защиту перед хостом, чтобы JVM получала только реальный трафик. У нас на MineGuard как раз такая антиддос-прослойка для Minecraft, но это уже другая история.

Дополнительная заметка о GC log в продакшене

Многие админы включают -Xlog:gc* ради красивых картинок в GCEasy и потом удивляются, что папка с логами выросла на 4 ГБ за неделю. Всегда ставьте rotation: filecount=5,filesize=20M достаточно для нормального анализа. Если запускаете staтистику на бета-инстансе, удобно слать GC events в Prometheus через jmx_exporter и потом смотреть на пики прямо в Grafana.

ZGC выводит в лог не только pause time, но и сегментированную информацию о фазах: Pause Mark Start, Concurrent Mark, Pause Mark End, Concurrent Reset Relocation Set, Pause Relocate Start, Concurrent Relocate. Если видите аномально длинную фазу Concurrent Mark, причина обычно одна: CPU starvation. На сервере, который ставится впритык, ZGC будет тормозить.

Когда вместо GC меняют код

Иногда правильный путь не выбор GC, а сам код. Если плагин делает new ArrayList<>() каждый тик на каждой сущности, никакой ZGC не спасёт от спайка allocation rate. Spark позволяет посмотреть allocation profile (/spark profiler --alloc), и часто видно ровно одного виновника, который генерирует половину объектов.

Старый совет 2010-х про reuse объектов в горячих циклах в Java 21 актуален меньше, escape analysis и stack allocation HotSpot-а делают своё дело. Но new HashMap<>() на каждый клик в InventoryClickEvent остаётся плохой идеей независимо от GC.

FAQ

Что лучше для 4 ГБ heap? G1GC с флагами Aikar. ZGC на маленьком хипе теряет смысл из-за overhead.

Можно ли на Java 21 просто включить ZGC и не думать? Почти. Не забудьте -XX:+ZGenerational, -Xms равен -Xmx, и оставьте RAM для ОС.

Я слышал, ZGC ест больше памяти. Насколько? 10-20% поверх рабочего set-а. На heap 16 ГБ это 2-3 ГБ дополнительной RAM. Заранее планируйте.

Modded-серверы на Forge поддерживают ZGC? Да, начиная с Forge 1.20+. Проверьте, что mod loader не делает странностей с classloader-ами. На Fabric и NeoForge всё работает чисто.

Stop-the-world пауза совсем исчезнет? Нет, в ZGC всё ещё есть короткие safepoint-ы (mark start, relocate start), но они занимают сотни микросекунд. Для тика в 50 мс это незаметно.

G1 окончательно умер? Никак нет. G1 остаётся хорошим дефолтом для большинства серверов до 12 ГБ. ZGC это специализированный инструмент для больших heap и high-load.

Выбор GC это инженерное решение, а не вопрос моды. На маленьком VPS с 6 ГБ RAM лучший вариант 2026 года всё ещё G1 с Aikar-флагами и Java 21. На крупном сервере с 32 ГБ heap, толпой игроков и постоянной чанк-генерацией Generational ZGC уберёт фризы и сделает игру плавнее. Профилируйте через Spark и GCEasy, не верьте чужим бенчмаркам без контекста, и помните, что плохо настроенный ZGC всегда проиграет хорошо настроенному G1.


Protégez votre serveur contre les attaques DDoS

Protection gratuite avec configuration en 5 minutes. 1 To de bande passante inclus.

Essayer gratuitement


Articles connexes