Витоки пам'яті під мікроскопом: Що відбувається в RAM, коли сервер працює занадто довго

Технічний посібник із дослідження оперативної пам'яті ігрових серверів. Розбір роботи Stack та Heap, лімітів збирача сміття та причин крашів Out Of Memory (OOM).

01.06.2026 Українська

Витоки пам'яті під мікроскопом: Що відбувається в RAM, коли сервер працює занадто довго

Симптоми цієї технічної проблеми знайомі кожному власнику ігрового сервера: відразу після перезавантаження ігровий світ працює бездоганно — TPS (тикрейт) тримається на максимумі, а задержки мінімальні. Проте через 12 годин сервер починає безпричинно «підфризувати», а ще через кілька годин операційна система хостингу повністю завершує його процес із лаконічною, але фатальною поміткою Out Of Memory (OOM).

Багато хто списує це на погану оптимізацію самої гри. Але щоб по-справжньому захистити проєкт від подібних падінь, необхідно заглянути під мікроскоп оперативної пам'яті (RAM) і зрозуміти, як ігровий рушій розподіляє ресурси, як працює вбудований збирач сміття (Garbage Collector) і чому в коді виникають приховані витоки.

1. Два стовпи пам'яті: Стек (Stack) проти Купи (Heap)

Коли ігровий сервер запущений, операційна система виділяє йому ізольований простір в RAM. Движок гри (будь то Unreal Engine, Unity чи Java) ділить цю пам'ять на дві принципово різні зони: Стек і Купу.

Стек (Stack) — швидка тимчасова пам'ять

Стек працює за принципом LIFO (останнім прийшов — першим пішов) і керується процесором автоматично на апаратному рівні. Тут зберігаються лише найпростіші типи даних та локальні змінні всередині функцій (наприклад, поточні координати кулі в момент пострілу або ID тимчасового діалогового вікна).

Щойно функція завершує свою роботу (куля долетіла до цілі, діалог закрився), пам'ять у стеку моментально і гарантовано очищається. Стек працює блискавично, але його розмір жорстко обмежений.

Купа (Heap) — глобальний динамічний хаос

Купа — це величезний масив пам'яті, де зберігаються важкі, довгоживучі та динамічно змінювані об'єкти. Абсолютно всі сутності ігрового світу — тривимірні будівлі гравців, інвентарі сотень користувачів, штучний інтелект ботів та масиви кешу бази даних — живуть саме в купі.

На відміну від стека, пам'ять у купі не може очиститися автоматично, коли функція завершена. Якщо гравець відкрив скриню, сервер виділяє в купі місце під масив предметів. Коли гравець закриває скриню, цей масив більше не потрібен, але він залишається лежати в купі доти, доки програма примусово його не видалить.


2. Як працює Збирач сміття (Garbage Collector)

У мовах програмування на кшталт Java (Minecraft) або C# (Unity / Rust) розробникам не потрібно вручную прописувати видалення кожного об'єкта з купи. Це завдання бере на себе спеціальний фоновий алгоритм — Garbage Collector (GC) або Збирач сміття.

Періодично GC «прокидається» і сканує купу. Він шукає об'єкти, до яких в коді гри більше немає жодного живого посилання. Якщо гравець вийшов із сервера, його об'єкт Player втрачає зв'язок з ігровим процесом. GC бачить цей занедбаний об'єкт, стирає його та звільняє RAM.

Чому збирач сміття викликає фризи (Stop-the-World)?

Щоб безпечно очистити купу, класичний збирач сміття зобов'язаний на частки мілісекунди повністю «заморозити» виконання основного ігрового потоку. Цей процес називається Stop-the-World (Зупинка світу). Якщо купа вашого сервера розрослася до 10–16 ГБ і забита мільйонами дрібних об'єктів, GC знадобиться занадто багато часу на її обхід. Для гравців це виглядає як раптові мікрофризи (заїкання) сервера кожні кілька хвилин.


3. Анатомія витоку пам'яті (Memory Leak)

Витік пам'яті в сучасних іграх — це не фізичне зникнення RAM, а ситуація, за якої збирач сміття не може видалити непотрібний об'єкт, тому що на нього помилково продовжує посилатися якась жива частина коду.

Розглянемо класичний приклад прихованого витоку в скриптах ігрового режиму:

  1. Ви написали скрипт системи логування, який записує дії гравців у глобальний масив (список) у пам'яті.
  2. Гравець здійснив дію, лог записався. Гравець вийшов із сервера.
  3. Логіка гри знищила об'єкт гравця, але запис про його дію залишився лежати в глобальному масиві.
  4. Оскільки цей масив є глобальним і ніколи не очищається, збирач сміття бачить: елемент масиву посилається на дані гравця. Значить, видаляти цю пам'ять не можна!

Через добу роботи через сервер проходять тисячі гравців. Купа заповнюється гігабайтами «фантомних» об'єктів, які фактично не використовуються грою, але утримуються в RAM через архітектурні помилки в коді плагінів.


4. Фінал катастрофи: Хто такий OOM Killer?

Оперативна пам'ять фізичного сервера на хостингу має жорстку межу (наприклад, 8 або 16 ГБ). Коли через витоки пам'яті купа ігрового рушія розростається до критичного максимуму, сервер запитує в операційної системи (зазвичай це Linux / Ubuntu) ще трохи RAM. Але вільної пам'яті на вузлі більше немає.

У цей момент на захист стабільності всієї операційної системи стає внутрішній механізм ядра Linux — OOM Killer (Out Of Memory Killer).

  • Його завдання — запобігти повному зависанню операційної системи через нестачу пам'яті.
  • OOM Killer сканує запущені процеси і вибирає «жертву» за спеціальним алгоритмом (враховується обсяг з'їденої RAM та час роботи).
  • Оскільки ігровий сервер у цей момент утилізує майже 100% виділеної пам'яті, OOM Killer миттєво відправляє йому сигнал SIGKILL (примусове жорстке завершення процесу).

Сервер миттєво вимикається без запису фінальних збережень у логи, залишаючи адміністратора перед фактом раптового падіння проєкту.

Стан RAM Що відбувається всередині сервера Вплив на ігровий процес
Стабільний (Норма) Купа містить лише активні об'єкти. Збирач сміття (GC) легко та швидко видаляє тимчасові дані за 1–2 мс. Ідеальний TPS (30.0 / 20.0), повна відсутність фризів та стабільний пінг.
Накопичення витоків Купа забита мільйонами «мертвих» об'єктів, що утримуються помилковими посиланнями. GC витрачає до 50–100 мс на сканування пам'яті. Періодичні короткочасні підвісання (Stuttering), під час яких сервер не відповідає на мережеві пакети.
Критичний (OOM) Пам'ять заповнена на 100%. Виділення нових областей під координати чи інвентар неможливе. Ядро ОС активує OOM Killer. Моментальне аварійне вимкнення сервера. В логах хостингу пишеться помилка Terminated або Error Code 137.

Головний висновок для адміністратора

Витоки пам'яті неможливо виправити збільшенням потужності процесора. Більше того, проста купівля тарифу з більшим обсягом RAM (наприклад, перехід з 8 ГБ на 16 ГБ) за наявності серйозного витоку в коді не вирішить проблему, а лише відстрочить момент крашу — сервер упаде не через 12 годин, а через 24.

Єдиний надійний спосіб боротьби з витоками пам'яті — концептуальна гігієна коду місій та плагінів: своєчасне видалення невикористовуваних елементів із глобальних масивів, очищення кешу бази даних та відмова від використання застарілих, занедбаних розробниками модифікацій.

Схожі статті

Віртуалізація та Обмеження ресурсів: Що відбувається, коли ваш сервер виходить за рамки лімітів хостингу

Технічний огляд роботи контейнеризації та підсистеми cgroups в Linux. Розбір процесорного троттлінгу (CFS) та ефекту «галасливих сусідів» на ігрових серверах.

Читати далі

Синхронність проти Асинхронності: Як бази даних визначають стабільність ігрового TPS

Технічний архітектурний посібник, що демонструє вплив синхронних запитів до БД на виникнення I/O Bottleneck та падіння TPS ігрових серверів.

Читати далі

Багатопотоковість в ігрових серверах: Головний міф про кількість ядер

Технічний архітектурний посібник, що розкриває однопотокові обмеження ігрових серверів. Дізнайтеся, чому тактова частота ядра важливіша за загальну кількість ядер.

Читати далі