Как найти утечку памяти в production за 5 минут: шпаргалка IT-инженера

Сервер тормозит из-за swap? Пошаговая инструкция: диагностика, поиск виновника, настройка swappiness, частые ошибки. Быстрые команды для системных администраторов.

3 часа ночи. Мониторинг орёт. Memory usage 95%. OOM killer начал убивать процессы. Босс в личке спрашивает ETA. Знакомо?

Эта шпаргалка — для тех, кто не хочет гуглить базовые команды, пока сервер умирает. Здесь только рабочие команды, которые можно скопировать и запустить прямо сейчас.

Время на диагностику: 5 минут. Время на фикс зависит от вашего кода (и от того, насколько сильно вы наследовали от легаси).

Когда это использовать

Реальные кейсы:

  • RAM растёт, не падает даже после рестарта трафика
  • Приложение медленно жрёт память и через N часов падает
  • После деплоя новой версии сервер начал пухнуть
  • Kubernetes постоянно перезапускает поды из-за OOM
  • Swap забит, система тормозит как Windows Vista

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

  • Смотрят только на free -h (и не понимают, что cache != leak)
  • Рестартят сервис вместо поиска причины
  • Не снимают heap dump до рестарта (и теряют все улики)
  • Путают memory leak с нормальным ростом кеша

Быстрый старт (если горит прям сейчас)

1. Найдите процесс-жрун:

ps aux --sort=-%mem | head -10

2. Проверьте динамику:

watch -n 1 'ps aux --sort=-%mem | head -5'

3. Снимите heap dump (для Java/Node.js/Python):

# Java
jmap -dump:live,format=b,file=/tmp/heap_dump.hprof <PID>

# Node.js
kill -USR2 <PID>  # если настроен heapdump модуль

# Python
py-spy dump --pid <PID> > /tmp/stack_trace.txt

4. Освободите память экстренно (если уже OOM):

sync && echo 3 > /proc/sys/vm/drop_caches

5. Рестартните сервис (после снятия дампа!):

systemctl restart your-app

Дальше — разбор дампа на dev-окружении. Не в проде. Пожалуйста.

Основные команды для диагностики

1. Общая картина памяти

Смотрим, что вообще происходит с памятью в системе.

free -h

Что смотреть:

  • available — реально доступная память (не total — used!)
  • buff/cache — это нормально, kernel использует свободную память
  • swap used — если >0, уже плохо

Если available < 10% от total — у вас проблема.

2. Поиск процесса-виновника

Самый жирный процесс по памяти:

ps aux --sort=-%mem | head -20

Или в реальном времени (обновление каждую секунду):

watch -n 1 &#039;ps aux --sort=-%mem | head -10&#039;

Если нужен топ с визуализацией — используйте htop:

htop
# Нажмите F6 -&gt; выберите PERCENT_MEM -&gt; Enter

3. Смотрим динамику роста памяти

Логируем потребление памяти конкретным процессом каждые 5 секунд:

PID=12345  # замените на реальный PID
while true; do 
  ps -p $PID -o %mem,rss,vsz,cmd | tail -1
  sleep 5
done

Если RSS (Resident Set Size) растёт линейно — у вас leak. Если скачет — это cache или нормальная работа GC.

4. Проверка memory maps процесса

Смотрим, куда утекает память внутри процесса:

pmap -x &lt;PID&gt; | sort -nrk3 | head -20

Или более детально:

cat /proc/&lt;PID&gt;/smaps | grep -A 15 &quot;^Size&quot; | head -50

Что искать: большие анонимные регионы (anon), которые растут.

5. Проверка открытых файлов (частая причина leak)

Приложение может жрать память через незакрытые файловые дескрипторы:

lsof -p &lt;PID&gt; | wc -l

Если число дескрипторов растёт — у вас file descriptor leak (который тоже ест память).

Детальный список:

lsof -p &lt;PID&gt; | grep -i mem

6. Heap dump для разных языков

Java (JVM)

Снять heap dump без остановки приложения:

jmap -dump:live,format=b,file=/tmp/heap.hprof &lt;PID&gt;

Быстрый анализ прямо в консоли:

jmap -histo:live &lt;PID&gt; | head -30

Если jmap не работает (бывает на production):

kill -3 &lt;PID&gt;  # Thread dump в stdout/логи

Node.js

Установите heapdump модуль заранее:

npm install heapdump

В коде приложения:

const heapdump = require(&#039;heapdump&#039;);
// Сигнал для снятия дампа
process.on(&#039;SIGUSR2&#039;, () =&gt; {
  heapdump.writeSnapshot((err, filename) =&gt; {
    console.log(&#039;Heap dump:&#039;, filename);
  });
});

Снять dump:

kill -USR2 &lt;PID&gt;

Альтернатива — использовать node —inspect и Chrome DevTools.

Python

Быстрый snapshot через py-spy (не требует изменений в коде):

pip install py-spy
py-spy dump --pid &lt;PID&gt;

Для детального анализа — tracemalloc (нужно добавить в код):

import tracemalloc
tracemalloc.start()

# ваш код

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics(&#039;lineno&#039;)
for stat in top_stats[:10]:
    print(stat)

Go

Если в приложении включён pprof:

curl http://localhost:6060/debug/pprof/heap &gt; heap.prof
go tool pprof -http=:8080 heap.prof

7. Анализ через valgrind (для C/C++)

Если у вас нативное приложение:

valgrind --leak-check=full --log-file=valgrind.log ./your_app

Внимание: valgrind замедляет приложение в 10-50 раз. Используйте только на dev/staging.

8. Системный мониторинг в реальном времени

Если нужно следить за несколькими процессами:

top -b -n 1 | grep -E &quot;(java|node|python|your_app)&quot;

Или более продвинутый вариант с glances:

pip install glances
glances

Glances показывает всё: CPU, RAM, I/O, network — в одном экране.

Частые ошибки и подводные камни

1. «Памяти нет, но free показывает много»

Проблема: Смотрят на «free» вместо «available».

Решение:

free -h
# Смотрите на строку &quot;available&quot;, а не &quot;free&quot;

Cache и buffers — это не утечка. Это нормальная работа kernel.

2. «Рестартнул сервис — памяти не прибавилось»

Проблема: Память не возвращается в систему сразу после kill процесса.

Решение: Принудительно очистите cache:

sync &amp;&amp; echo 3 &gt; /proc/sys/vm/drop_caches

Или просто подождите 30-60 секунд — kernel сам освободит.

3. «PID меняется после рестарта, скрипты ломаются»

Проблема: Хардкодят PID в скриптах мониторинга.

Решение: Используйте pidof или systemctl:

PID=$(pidof your-app)
# или
PID=$(systemctl show --property MainPID --value your-app)

4. «Heap dump весит 10GB, сервер умер»

Проблема: Снимают dump на проде с полным heap.

Решение: Используйте live объекты:

jmap -dump:live,format=b,file=/tmp/heap.hprof &lt;PID&gt;

Флаг live запускает GC перед дампом и сохраняет только живые объекты.

5. «Memory leak только на production»

Проблема: На dev окружении нагрузка в 100 раз меньше.

Решение: Используйте staging с реальным объёмом трафика. Или нагрузочное тестирование:

# Apache Bench
ab -n 100000 -c 100 http://your-app/endpoint

# wrk (более продвинутый)
wrk -t12 -c400 -d30s http://your-app/endpoint

Leak проявится быстрее.

Полезные хаки

1. Алиас для быстрого поиска жирных процессов

Добавьте в ~/.bashrc:

alias memtop=&#039;ps aux --sort=-%mem | head -20&#039;
alias memwatch=&#039;watch -n 1 &quot;ps aux --sort=-%mem | head -10&quot;&#039;

Теперь просто пишете memtop — и всё перед глазами.

2. Логирование памяти в файл

Скрипт для автоматического мониторинга:

#!/bin/bash
LOG_FILE=&quot;/var/log/memory_monitor.log&quot;
PID=$(pidof your-app)

while true; do
  TIMESTAMP=$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)
  MEM=$(ps -p $PID -o %mem,rss | tail -1)
  echo &quot;$TIMESTAMP - $MEM&quot; &gt;&gt; $LOG_FILE
  sleep 60
done

Запустите в screen/tmux:

screen -dmS memlog <a class="wpil_keyword_link" href="https://it-apteka.com/tag/bash/"   title="Bash" data-wpil-keyword-link="linked"  data-wpil-monitor-id="226">bash</a> /path/to/monitor.sh

3. Уведомление в Slack при превышении лимита

#!/bin/bash
THRESHOLD=80  # процент памяти
WEBHOOK_URL=&quot;https://hooks.slack.com/services/YOUR/WEBHOOK&quot;

MEM_USAGE=$(free | grep Mem | awk &#039;{print int($3/$2 * 100)}&#039;)

if [ $MEM_USAGE -gt $THRESHOLD ]; then
  curl -X POST -H &#039;Content-type: application/json&#039; \
    --data &quot;{\&quot;text\&quot;:\&quot;⚠️ Memory usage: ${MEM_USAGE}%\&quot;}&quot; \
    $WEBHOOK_URL
fi

Добавьте в cron на запуск каждые 5 минут:

*/5 * * * * /path/to/mem_alert.sh

4. Быстрый анализ Java heap dump без GUI

Если нет доступа к Eclipse MAT:

# Установите jhat (входит в JDK)
jhat -J-Xmx4G /tmp/heap.hprof

# Откройте в браузере
# http://localhost:7000

Или используйте консольный анализатор:

jmap -histo /tmp/heap.hprof | head -50

5. Ограничение памяти через systemd

Чтобы процесс не сожрал весь сервер:

# /etc/systemd/system/your-app.service
[Service]
MemoryLimit=2G
MemoryMax=2G

Перезагрузите конфиг:

systemctl daemon-reload
systemctl restart your-app

Теперь при превышении 2GB процесс будет убит OOM killer.

6. cgroup для изоляции памяти

Если systemd нет или нужен более гибкий контроль:

# Создаём cgroup
cgcreate -g memory:/myapp

# Лимит 2GB
echo 2147483648 &gt; /sys/fs/cgroup/memory/myapp/memory.limit_in_bytes

# Запускаем процесс в cgroup
cgexec -g memory:/myapp /path/to/your-app

Проверка и диагностика

Чек-лист перед завершением диагностики

1. Подтвердите leak (а не нормальный рост):

# Запустите &lt;a class=&quot;wpil_keyword_link&quot; title=&quot;Мониторинг&quot; href=&quot;https://it-apteka.com/category/monitoring/&quot; data-wpil-keyword-link=&quot;linked&quot; data-wpil-monitor-id=&quot;212&quot;&gt;мониторинг&lt;/a&gt; на 10 минут
watch -n 10 &#039;ps aux --sort=-%mem | head -5&#039;

Если RSS растёт линейно без падений — это leak.

2. Проверьте логи приложения:

tail -f /var/log/your-app/error.log | grep -i &quot;memory\|oom\|allocation&quot;

3. Проверьте system logs:

dmesg | grep -i &quot;out of memory\|oom&quot;
journalctl -xe | grep -i oom

Если видите «Out of memory: Killed process» — kernel уже начал убивать процессы.

4. Сохраните все дампы и логи:

mkdir -p /var/backups/memory_incident_$(date +%Y%m%d_%H%M%S)
cp /tmp/*.hprof /tmp/*.prof /var/log/your-app/* /var/backups/memory_incident_*/

5. Задокументируйте инцидент:

  • Когда началось
  • Какой процесс
  • Сколько памяти жрало
  • Что изменилось перед этим (деплой, конфиг, трафик)
  • Что помогло (рестарт, патч, откат)

Где смотреть логи

System logs:

# OOM events
dmesg -T | grep -i oom

# Systemd journal
journalctl -u your-app --since &quot;1 hour ago&quot;

# Kernel logs
tail -f /var/log/kern.log

Application logs:

# Стандартные локации
/var/log/your-app/
~/.pm2/logs/  # для Node.js с PM2
/var/log/nginx/error.log  # если leak в nginx

Краткий чек-лист

Что должно быть сделано:

  • ✅ Найден процесс с утечкой (ps aux —sort=-%mem)
  • ✅ Подтверждена динамика роста (watch на 5-10 минут)
  • ✅ Снят heap dump ДО рестарта
  • ✅ Проверены логи на OOM events
  • ✅ Сохранены все дампы и метрики

Что проверить перед продом:

  • ✅ Нагрузочное тестирование с мониторингом памяти
  • ✅ Настроены алерты на memory usage >80%
  • ✅ Включён автоматический heap dump при OOM (для Java)
  • ✅ Ограничения памяти через systemd/cgroup
  • ✅ Логирование метрик памяти в monitoring (Prometheus/Grafana)

Что сохранить себе:

  • Скрипты мониторинга памяти
  • Алиасы для быстрой диагностики
  • Команды для heap dump вашего стека
  • Ссылку на эту шпаргалку

Резюме

Утечка памяти в production — это не катастрофа, если знаешь, что делать. Главное:

  • Быстро найти виновника (ps aux —sort=-%mem)
  • Снять heap dump до рестарта (иначе потеряете улики)
  • Не паниковать (cache != leak)
  • Автоматизировать мониторинг (чтобы в следующий раз проснуться раньше OOM killer)

Эта шпаргалка спасала прод чаще, чем бэкапы. Добавьте её в закладки — она вам ещё пригодится. Обычно в 3 часа ночи.

В следующей шпаргалке разберём как найти CPU bottleneck за 5 минут. Потому что после memory leak обычно начинаются проблемы с процессором.

Поделитесь:

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх