Представьте четырёх друзей на сервере с большой сборкой. Трое неделями строят и налаживают автоматику на базе, а четвёртый получил новый компас и убежал в сторону нетронутой карты. Через минуту он пересёк границу сгенерированной территории, и сервер вместо привычной работы начал создавать землю, структуры, биомы и всё, что к ним добавили моды. Игроков по-прежнему четверо, но нагрузка уже другая. Слово «четверо» ничего о ней не говорит.
Поэтому вопрос «сколько гигабайт нужно на игрока» в модифицированном Minecraft почти бессмыслен. Правильный вопрос звучит иначе: что делает именно наша сборка в тех ситуациях, в которых наша компания обычно играет? Ответ придётся получить не из чужой таблицы, а из небольшого эксперимента. Ниже описано, как его провести, что измерять, как отличить нехватку памяти от других проблем и как из полученных наблюдений выбрать запас на VPS.
Три разные вещи, которые обычно называют «нагрузкой»
Когда говорят «тяжёлый сервер», смешивают три независимых источника расхода. Их полезно разделить сразу, потому что каждый из них проявляется в своё время и лечится по-своему.
Состав сборки. Это то, что сервер платит просто за существование. Каждый мод регистрирует блоки, предметы, рецепты, сущности, обработчики событий, иногда собственные структуры данных и таблицы генерации. Сборка из нескольких сотен модов занимает память ещё до того, как кто-то зашёл в игру. Эта часть почти не зависит от людей: она появляется на старте и остаётся примерно постоянной на протяжении сессии.
Настройки и состояние мира. Дальность прорисовки на сервере, дальность симуляции, число загруженных чанков, плотность построек и механизмов, количество сущностей и предметов на земле. Мир, в котором существует огромная фабрика с сотнями машин, и мир с одним домиком на той же сборке нагружают сервер по-разному, хотя состав модов одинаков. Эта часть растёт со временем жизни мира, и поэтому любой замер на свежем мире со временем устаревает.
Поведение игроков в данный момент. Кто-то стоит на базе, кто-то бежит по свежей местности, кто-то телепортируется между далёкими точками, кто-то запускает большую машину. Здесь важно не количество людей, а то, насколько их места не совпадают: четверо, стоящие рядом, держат загруженной одну область, а четверо, разошедшиеся в разные стороны, — четыре области сразу.
Из этих трёх слагаемых только первое одинаково у всех, кто поставил ту же сборку. Остальные принадлежат вашему миру и вашей компании. Вот почему цифра из обсуждения на форуме годится максимум как повод для первого запуска, но не как ответ.
Контрольный мир: на чём ставить опыт
Идея простая: до покупки конфигурации на длительный срок развернуть ту же сборку и прогнать на ней две-три заранее продуманные сцены, не вмешиваясь в основной мир. Чтобы результаты чего-то стоили, контрольный стенд должен быть честным.
Первое — использовать серверную поставку модпака. Если автор сборки выпускает server pack, берите его: документация NeoForge называет именно такой вариант предпочтительным для модпаков и напоминает, что клиентские визуальные моды на сервере могут быть не нужны (NeoForge: сервер и модпаки). Это не просто вопрос аккуратности. Клиентские моды, перенесённые на сервер без нужды, либо вызывают ошибки при старте, либо тихо занимают память под то, чем сервер никогда не воспользуется. Тогда замер покажет завышенную цифру, и вы переплатите за ресурс, которым игра не пользуется.
Второе — версия Java. Её выбирают под конкретную сборку, а не по принципу «поставим самую свежую» (NeoForge: сервер и модпаки). Если вы замеряете на одной версии, а потом запускаете на другой, сравнение теряет смысл: разные версии по-разному ведут себя со сборкой мусора и с нативными расходами.
Третье — параметры памяти JVM. Они задаются в файле user_jvm_args.txt (NeoForge: сервер и модпаки). Туда записывается верхняя граница heap, то есть той части памяти, которой управляет сборщик мусора Java. Для первого прогона выберите заведомо щедрое значение: цель на этом этапе — не втиснуться в рамки, а увидеть, сколько сборка захочет, когда ей не мешают. Ужимать будем потом, по наблюдениям.
Четвёртое — копия мира. Документация требует делать резервную копию перед обновлением NeoForge или модов (NeoForge: сервер и модпаки). Для контрольного стенда это правило работает вдвойне: вы будете гонять сервер по новым областям и многократно перезапускать, поэтому используйте копию, а не живой мир друзей. Если нужной копии мира нет, начните с нового мира, но тогда помните, что плотность построек в нём ничтожна, и результат будет оптимистичным для базы.
Пятое — инструмент измерений. В примерах ниже используется spark, мод-профилировщик с набором команд диагностики. Ничего другого, кроме него и обычного диспетчера процессов вашей операционной системы, для этой задачи не требуется. Если первичная установка Minecraft на VPS вам ещё не знакома, сначала пройдите материал про установку и настройку Minecraft на VPS, а затем возвращайтесь к замерам.
Что показывает spark и чем эти показания различаются
У spark несколько команд, и важно понимать, что каждая отвечает на свой вопрос. Согласно документации, health, tps, ping и gc показывают разные стороны состояния: ресурсы, тики, сетевую задержку и сборку мусора (spark: команды диагностики). Если читать их по отдельности, легко прийти к неверному выводу. Читать надо вместе.
health— общая картина использования ресурсов процессом и системой.tps— состояние игровых тиков: сколько их в секунду и сколько времени занимает каждый.ping— задержка между клиентами и сервером. Это сеть, а не память, и её нужно исключать из рассуждений о тормозах.gc— сведения о сборке мусора: как часто она происходит и сколько времени отнимает.
Теперь о главной метрике игрового тормоза. TPS — число тиков в секунду, MSPT — длительность одного тика. В обычном режиме при 20 TPS на обработку тика отводится 50 мс, и средняя длительность должна в них укладываться; spark показывает также медиану и 95-й процентиль (spark: TPS и MSPT). Почему это важно именно для разговора о памяти? Потому что память вредит игре не тем, что её «мало» в абстрактном смысле, а тем, что из-за неё тик занимает больше 50 мс. Всё остальное — следствия.
Обратите внимание на 95-й процентиль. Среднее значение MSPT может выглядеть прилично, если большинство тиков короткие, а редкие тики растягиваются на сотни миллисекунд. Игрок при этом чувствует именно эти редкие тики как рывки: летящая лодка замирает, блок возвращается на место, дверь открывается с задержкой. Поэтому на стенде вас интересует не только среднее, но и «хвост» распределения.
Сцена первая: запуск и спокойная база
Первая сцена состоит из двух частей, и их стоит наблюдать отдельно.
Запуск. Сервер стартует, загружает моды, регистрирует содержимое, готовит стартовые области. После выхода на состояние «можно подключаться» запишите, сколько heap занято. Это базовая цена сборки. Её можно сравнить с тем, что вы задали в user_jvm_args.txt: если после старта занята половина выделенного, вы ещё не знаете много, а если почти всё — значит, предел выбран слишком скромно для вашей сборки, и все дальнейшие измерения будут шумом сборщика мусора.
Спокойная игра. Подключитесь с одним-двумя клиентами и проведите четверть часа на базе: работайте у механизмов, обходите помещения, пользуйтесь хранилищами. Если в контрольном мире нет настоящей базы, постройте несколько типичных для вашей компании установок — автоматику добычи, ферму, подсистему хранения. Затем посмотрите tps и gc. Что искать:
- MSPT в 95-м процентиле стабильно ниже 50 мс;
- занятая heap колеблется «пилой»: растёт между сборками мусора и заметно падает после них;
- после падения нижняя точка пилы не ползёт вверх от цикла к циклу.
Последний пункт — самый информативный. Нижняя точка пилы показывает, сколько памяти действительно живёт в мире, а не просто временно накопилось в виде мусора. Если она держится на одном уровне, то задержки между сборками — это нормальный ритм работы, а не утечка и не нехватка.
Сцена вторая: исследование новых областей
Теперь воспроизведём то, с чего началась статья. Один-два игрока уходят в нетронутую часть мира. Лучше двигаться быстро, как это делают в реальности: на транспорте или с помощью модов перемещения, если они есть в сборке. Сначала по прямой, потом в стороны, чтобы области не совпадали.
Почему именно эта сцена показательнее всего? Потому что на новых территориях сервер выполняет работу, которой на базе нет: генерирует рельеф, расставляет структуры, применяет правила биомов из установленных модов, создаёт заготовки сущностей. Всё это превращается и во временные объекты, то есть в мусор для сборщика, и в новые загруженные чанки, то есть в долгоживущие данные. На спокойной базе чанки уже созданы, и сервер просто их поддерживает.
Во время такой пробежки полезно включить профилирование. По документации, команда profiler start принимает параметр --timeout, который ограничивает длительность профиля, а --only-ticks-over позволяет собирать только длительные тики; завершить и сохранить результат без загрузки на сайт можно командой profiler stop --save-to-file (spark: команды диагностики). На практике это значит, что вы запускаете ограниченный по времени профиль на время пробежки и сохраняете файл локально, а не гоняете профиль без конца и не отправляете его наружу. Фильтр по длинным тикам оставляет в профиле именно те моменты, из-за которых игрок почувствовал рывок.
Затем разбирайте отчёт по вопросам:
- Что занимало время в долгих тиках — генерация чанков, сущности, блочные сущности, конкретный мод?
- Совпали ли по времени долгие тики с активностью сборщика мусора, которую показывает
gc? - Выросла ли нижняя точка «пилы» heap по сравнению со спокойной сценой?
Если ответ на второй вопрос «да», а на третий — «нет», перед вами, скорее всего, не нехватка памяти, а дорогая генерация, которая сама по себе нагружает процессор и временно создаёт много мусора. Если «да» на оба — heap действительно упирается в потолок: загруженные данные не помещаются, и сборщик вынужден работать почти непрерывно.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.
Как выглядит сравнение двух сцен
Чтобы показать, как читать результаты, возьмём условный пример: числа ниже придуманы для иллюстрации и не являются замером какой-либо сборки. Допустим, на одной и той же сборке вы получили такие наблюдения.
| Что наблюдаем | Сцена 1: спокойная база | Сцена 2: новые области |
|---|---|---|
| Нижняя точка heap после сборки мусора | около 3 ГБ | растёт от 3 до 4,5 ГБ за пробежку |
| 95-й процентиль MSPT | около 35 мс | местами выше 50 мс |
| Паузы сборки мусора | редкие и короткие | частые, совпадают с долгими тиками |
| Что делает основную работу в профиле | механизмы и сущности | генерация чанков |
В этой картине видно сразу три вещи. Во-первых, спокойная сцена укладывается в бюджет тика с запасом, а значит, судя только по ней, вы бы уверенно выбрали меньший объём, и этот выбор оказался бы ошибкой. Во-вторых, во второй сцене рост нижней точки означает, что новые чанки накапливаются в памяти; чем дальше пробежка, тем больше нужно. В-третьих, рост MSPT связан с генерацией: если профиль показывает, что основное время уходит на неё, дополнительный heap смягчит паузы сборщика, но не сделает генерацию бесплатной.
Заметьте, что никакой «нормы на игрока» здесь не возникло. Сравнивались не люди, а сцены. Если в вашей компании обычно кто-то один исследует, а остальные работают на базе, это одна нагрузка; если все четверо идут в разные стороны, это другая, и её нужно проверить отдельно, запустив четыре клиента или хотя бы попросив друзей поучаствовать в тестовой пробежке.
Heap — это не вся память процесса, и тем более не вся память машины
Здесь чаще всего ошибаются при покупке. Значение, которое вы записали в user_jvm_args.txt, ограничивает heap. Но процесс Java занимает больше, чем heap, а машина — больше, чем процесс Java. Представьте три вложенные коробки.
Внутренняя коробка — heap. Здесь живут объекты игры: чанки, сущности, структуры данных модов. Её размер вы выбираете сами, и именно её заполнение показывают инструменты вроде spark.
Средняя коробка — весь процесс Java. Кроме heap в неё входят нативные расходы: служебные области для загруженных классов, стеки потоков, буферы для сетевого и файлового ввода-вывода, кэш скомпилированного кода и память, которую напрямую запрашивают нативные библиотеки. Сборка с большим числом модов загружает много классов и создаёт много потоков, поэтому эта прослойка не пренебрежимо мала. Её размер вы не задаёте одной цифрой, но можете увидеть: сравните резидентную память процесса в диспетчере ОС с занятым heap, и разница подскажет, сколько приходится на всё остальное. Сравнивать нужно в обеих сценах, потому что на исследовании растёт и то, и другое.
Внешняя коробка — машина. Кроме Java на сервере работает операционная система, а вместе с ней — служебные процессы, доступ по SSH, возможно, скрипты резервного копирования. Кроме того, система использует свободную память под файловый кэш: мир Minecraft состоит из множества файлов, и если для них есть место в кэше, чтение и запись идут быстрее. Если отдать всю память под heap, кэшу ничего не останется, а при нехватке системе придётся выгружать данные или завершать процессы. Обычно это выглядит как внезапное падение сервера без внятной ошибки в игре.
Практический смысл такой: объём памяти VPS должен быть больше, чем heap, и разница определяется наблюдениями, а не правилом «плюс столько-то». Вы измеряете процесс в самой тяжёлой сцене, смотрите, сколько он реально занимает, добавляете то, что нужно системе и кэшу, и только потом сравниваете с ресурсами тарифа. Если при тестовой пробежке система уже уходит в подкачку или процесс вытесняется, значит, запас был выбран слишком тугим, даже если heap выглядит спокойно.
Отдельная осторожность — дамп heap. Spark умеет его снимать, но документация предупреждает, что файл может занимать много места и содержать данные процесса (spark: команды диагностики). Для оценки объёма памяти он обычно не нужен: достаточно динамики и профиля. Если всё же снимаете, проверьте свободное место на диске и не пересылайте файл посторонним, потому что вместе с игровыми объектами в нём может оказаться то, чем вы не хотели делиться.
Когда увеличение heap не помогает
Есть соблазн лечить любые тормоза гигабайтами. Иногда это работает, но чаще всего только откладывает проблему или вообще ничего не меняет. Вот признаки того, что узкое место не в объёме heap.
Нижняя точка «пилы» не растёт, а MSPT высокий. Памяти достаточно, но тик всё равно длиннее 50 мс. Значит, тик занят вычислениями: машина, цепочка механизмов, огромная ферма или конкретный мод, который делает слишком много за тик. Профиль с фильтром --only-ticks-over покажет, кто именно. Лечение — упростить установку, ограничить число сущностей, обновить или заменить проблемный мод, а не добавлять память. В этом случае среднее значение TPS снижается, пока gc остаётся спокойным.
Сборщик мусора работает редко, но MSPT дёргается только при генерации. Это стоимость создания новой территории, и она зависит прежде всего от производительности процессора и диска. Увеличив heap вдвое, вы получите ту же генерацию с чуть менее частыми паузами. Реальные способы справиться с этим — заранее сгенерировать область, по которой вы собираетесь ходить, или принять, что исследование на этой сборке всегда будет немного рывками.
Задержка высока, а тики в норме. Если tps показывает здоровые значения, а игроки жалуются на лаги, сравните с ping. Это сетевая задержка между клиентом и сервером; память здесь ни при чём. Решение — выбор расположения сервера ближе к игрокам, а не увеличение heap. Это тот случай, когда расположение VPS влияет на ощущения сильнее, чем его объём.
Слишком большой heap ухудшает ситуацию. Когда heap огромен, а в нём накопилось много мусора, редкие сборки становятся длиннее, потому что собирать приходится больше. Поэтому схема «поставим побольше, хуже не будет» не гарантирует плавности: чрезмерный запас бывает такой же ошибкой, как недостаточный. Проверяйте на стенде именно длительность пауз в gc при разных значениях, а не только факт, что сервер не упал.
Нижняя точка растёт от пробежки к пробежке и не возвращается. Если после возвращения игроков на базу и выдержки времени нижняя точка остаётся высокой, возможна утечка в одном из модов или накопление загруженных данных. Добавление памяти лишь отодвинет момент проблемы. Здесь нужен профиль и, при необходимости, сужение подозреваемых модов бинарным поиском по копии сборки.
Общий принцип такой: память нужна, когда нижняя точка heap упирается в предел и сборщик мусора работает постоянно. Во всех остальных случаях сначала определите, что именно удлиняет тик, и только потом решайте, чем это лечить.
Как выбрать запас под характерную сессию
Сопоставьте результаты стенда с реальным поведением вашей компании. Полезно описать три-четыре типичных сессии одной фразой каждая: «вечер на базе, двое налаживают автоматику», «первый день после обновления — все бегут смотреть новые биомы», «большая стройка с одновременным запуском фабрики». Для каждой найдите соответствующую сцену из эксперимента. Наиболее требовательная из тех, которые происходят регулярно, а не раз в полгода, становится вашей расчётной нагрузкой.
Дальше сформулируйте требования по наблюдениям:
- heap должен вмещать нижнюю точку расчётной сцены с заметным запасом, чтобы сборщик не работал на пределе;
- для процесса сверху нужен запас на нативные расходы, который вы увидели в диспетчере;
- для системы и файлового кэша тоже нужен свободный объём, который остаётся после этих двух;
- отдельно оставьте допуск на рост мира: через месяц игры построек станет больше, а нижняя точка сместится вверх.
Теперь можно открыть список тарифов. Для оценки удобны ресурсы из каталога VPS MAATRIX: Ba — 1 vCPU и 2 ГБ RAM, Uab — 2 vCPU и 4 ГБ, Heka — 4 vCPU и 8 ГБ, Maat — 8 vCPU и 16 ГБ, Djed — 16 vCPU и 32 ГБ. Эти цифры — отправная точка для теста, а не обещание, что на определённом тарифе уместится заданное число игроков или конкретная сборка. Просто сравните свою расчётную сцену с суммой «heap плюс нативные расходы плюс запас системы» и посмотрите, какая строка таблицы вмещает её без уговоров. Кстати, объём — только одно измерение: если профиль показал, что тик занят вычислениями, смотрите и на число vCPU, а не только на гигабайты. В публичном описании указаны NVMe-диски и процессоры AMD EPYC, а также root-доступ, который понадобится, чтобы поставить нужную версию Java и настроить сервер под сборку; для мира, который постоянно читается и записывается при исследовании, быстрый диск тоже входит в картину.
Практичнее всего провести ту же проверку на выбранном сервере до окончательного переезда: перенесите server pack, повторите обе сцены и сравните с тем, что видели на стенде. Если расхождение небольшое, вы подобрали ресурсы верно. Если значительное, причина обычно в одном из трёх: другая версия Java, иное значение в user_jvm_args.txt или иная конфигурация мира. Каждая из причин видна по тем же наблюдениям, которые вы уже умеете собирать.
Перед любым обновлением NeoForge или модов делайте копию мира, как требует документация (NeoForge: сервер и модпаки), и после каждого обновления повторяйте хотя бы вторую сцену. Новая версия мода способна заметно поменять стоимость генерации, и прежний вывод о запасе после неё может оказаться неверным.
Что на самом деле покупается
Вернёмся к четырём друзьям. Трое работают на базе, один убежал вперёд. Для сервера это не «четыре игрока», а спокойная область, где всё давно загружено и привычно, и свежая территория, которую придётся создавать с нуля через весь набор установленных модов. Первая сцена почти бесплатна, вторая определяет, хватит ли вам запаса. Если вы измерили обе, вы знаете то, чего не скажет ни одна таблица слотов: как ведёт себя ваша сборка, в вашем мире, у вашей компании.
Поэтому ресурсы стоит покупать под мир и привычки группы. Если ваши друзья любят исследовать вместе, закладывайте это в расчётную сцену; если они тихо строят одну базу годами, ориентируйтесь на её рост. Красивая цифра слотов в описании тарифа никого не освободит от долгого тика, а три хороших замера избавят и от переплаты, и от вечера, когда сервер рывками рисует новые горы.