Борьба с утечками памяти (Memory Leaks) в AMX-скриптах
Стабильная непрерывная работа сервера без перезагрузок в течение нескольких дней или недель — признак качественного игрового мода и правильно настроенного хостинга. Однако многие администраторы SA:MP/CRMP серверов сталкиваются со следующей проблемой: после рестарта server работает идеально, но спустя 12–24 часа потребление оперативной памяти (RAM) контейнером хостинга вырастает до максимума, и панель управления принудительно отключает процесс сервера с критической ошибкой Out Of Memory (OOM Killer).
Причиной этого являются утечки памяти (Memory Leaks). В этой статье мы разберем анатомию памяти скриптового движка Pawn, найдем скрытые ловушки в коде, которые незаметно «сжирают» оперативную память, и научимся их устранять.
Как устроена память в виртуальной машине AMX?
Язык Pawn является надстройкой над виртуальной машиной AMX. В отличие от C++ или C#, здесь базовая память статична. Когда вы компилируете мод, компилятор заранее выделяет фиксированные массивы под глобальные и локальные переменные (сегменты данных, стека и кучи). Из этого следует важный вывод:
Сам по себе чистый Pawn-код не может вызвать утечку памяти, так как размер вашего файла .amx и его базовые требования к RAM фиксируются в момент компиляции.
Откуда же берутся утечки? Они возникают в двух случаях: при некорректном использовании динамической памяти в сторонних плагинах (MySQL, Стример, RakNet) и при переполнении пула динамических зон (например, динамических текстдравов или 3D-текстов).
Главные виновники утечек памяти на игровом сервере
1. Забытые кэши в плагине MySQL (Самая частая причина)
Современные версии плагина MySQL (BlueG R39+) при отправке асинхронных запросов через mysql_tquery или mysql_pquery выделяют область памяти под результат ответа (кэш). Если внутри вашего колбэка вы не очистите этот кэш, память останется заблокированной навсегда.
Если на ваш сервер заходит 200 игроков в час, и при каждом заходе кэш авторизации не очищается, сервер будет терять по 50–100 МБ оперативной памяти ежедневно.
Как исправить: В конце каждого колбэка, который обрабатывает ответ от базы данных, где используется создание кастомного кэша, или если вы вручную переключаете контекст через cache_set_active, обязательно вызывайте функцию очистки:
forward OnPlayerLogin(playerid);
public OnPlayerLogin(playerid)
{
cache_delete(Cache:cache_id);
return 1;
}
2. Бесконечный спавн динамических объектов в Стримере (Incognito)
Плагин Streamer позволяет обходить лимиты SA:MP на объекты, чекпоинты и 3D-тексты, создавая их динамически. Однако, если вы создаете объект функцией CreateDynamicObject внутри циклического таймера или при каждом входе игрока, но забываете его удалить при выходе (DestroyDynamicObject), память плагина начнет стремительно забиваться структурами данных.
Пример плохой практики: Создание персональной иконки на карте или 3D-текста над головой при авторизации без последующего удаления в OnPlayerDisconnect. Спустя 500 переподключений игроков память сервера будет перегружена тысячами «фантомных» объектов, которые сервер продолжает просчитывать в фоне.
3. Форматирование строк и утечки в стэке (Stack/Heap Stretch)
Если в вашем моде активно используются рекурсивные функции или функции с огромными локальными массивами под форматирование текста (например, new string[65536]; внутри часто вызываемого таймера), это приводит к растяжению сегмента Stack/Heap.
Движок AMX автоматически расширяет кучу (Heap), если локальным переменным не хватает места, но он никогда не сужает её обратно в процессе работы сервера. Один раз вызванная функция с избыточным размером массива навсегда заберет этот кусок RAM у хостинга.
Правило оптимизации: Никогда не создавайте массивы «с запасом». Для стандартного форматирования строки чата достаточно string[144], для диалога — от 512 до 2048. Используйте оператор static для массивов в таймерах, чтобы память под них выделялась один раз при запуске, а не плодилась каждый тик.
Методология выявления скрытых утечек
Если сервер уже лагает и «жрет» память, найти конкретную строчку кода вручную среди 50 000+ строк мода невозможно. Используйте профессиональный инструмент локализации проблемы:
Шаг 1: Подключение плагина Profiler
Скачайте и установите плагин Profiler (от разработчика Zeex) на ваш сервер хостинга. Он предназначен для детального аудита работы AMX-машины.
- Загрузите
profiler.soв папкуplugins/. - Пропишите его первым в строке
pluginsв файлеserver.cfg. - Запустите сервер на 2–3 часа в режиме обычного игрового процесса.
Плагин запишет файл лога, в котором покажет, какие функции вызывались чаще всего и сколько памяти они утилизировали. Если вы увидите, что куча (Heap) растет после вызова определенного колбэка — вы нашли источник проблемы.
Шаг 2: Мониторинг динамических лимитов через консоль
Периодически вводите в консоль сервера команды проверки состояния Стримера, чтобы увидеть, не растут ли показатели объектов бесконтрольно:
Streamer_GetUpperBound(STREAMER_TYPE_OBJECT);— покажет текущее количество динамических объектов в памяти. Если это число увеличивается при неизменном онлайне — в коде есть утечка создания объектов.
Шаг 3: Проверка логов MySQL на "Зависшие" кэши
Откройте файл mysql_log.txt (убедитесь, что в mysql_log включен режим error или warning). Если плагин MySQL фиксирует утечки памяти, вы увидите системные предупреждения вида:
[WARNING] MySQL::Destroy - active cache was not deleted
Это прямое указание на то, что один из ваших SQL-запросов успешно выполнился, но вы забыли написать cache_delete в его колбэке. Проверьте все последние добавленные системы (донат, инвентарь, логирование) — корень зла находится там.
| Тип ресурса | Что вызывает утечку памяти? | Как правильно удалять / очищать |
|---|---|---|
| MySQL Результаты | Пропуск очистки ответов СУБД | cache_delete(Cache:...) в конце колбэка |
| Динамические 3D Тексты | Создание поверх старых при спавне игрока | DestroyDynamic3DTextLabel перед созданием нового |
| Текстдравы (TextDraw) | Использование глобальных TD как персональных | Используйте CreatePlayerTextDraw (они удаляются автоматически) |