Акция Панель управления ispmanager для VDS — первый месяц бесплатно
до 31.07.2026 Подробнее
Выберите продукт

Linux memory leaks: smem, smaps_rollup, pmap и правильный разбор RSS

Когда процесс в Linux «жрёт память», важно понять, что именно растёт: утечка в heap, кэш аллокатора или file-backed страницы. Разберём RSS vs PSS, smem и pmap, /proc/PID/status, smaps и smaps_rollup, чтобы увидеть динамику anon/file/shmem и связать её с поведением приложения.
Linux memory leaks: smem, smaps_rollup, pmap и правильный разбор RSS

Что мы называем «утечкой памяти» в Linux

На практике «linux memory leak» почти всегда сводится к одному из сценариев:

  • Настоящая утечка: приложение удерживает ссылки на объекты/буферы и реально накапливает память.
  • Кэш/арены аллокатора (glibc malloc, jemalloc, tcmalloc): память возвращается приложению, но не всегда отдаётся ядру; RSS выглядит «липким».
  • Рост mmap: приложение мапит анонимные регионы (например, большие буферы, JIT, базы, очереди) и не освобождает или удерживает их слишком долго.
  • File-backed страницы: процесс активно читает файлы, RSS растёт, но значительная часть — страницы, которые ядро может вытеснить при давлении по RAM.
  • Shared memory: tmpfs, SysV/Posix SHM, memfd; RSS «у всех», а физически это может быть одна копия.

Проблема в том, что мониторинги часто показывают один показатель (обычно RSS), а он не отвечает на вопрос «кто виноват» и «куда ушла память». Дальше — набор инструментов, которые помогают разложить потребление по составляющим.

RSS vs PSS: почему один и тот же процесс «ест» по-разному

RSS (Resident Set Size) — сколько страниц процесса сейчас находится в RAM. Если страница шарится (общие библиотеки, shared memory), она попадёт в RSS каждого процесса, который её мапит.

PSS (Proportional Set Size) — «пропорциональный» RSS: шаренная страница делится между процессами. Если страницу делят 4 процесса, то каждому добавится по 1/4 страницы в PSS. Для оценки «кто сколько реально занимает физической памяти» PSS почти всегда полезнее.

Если у вас несколько воркеров (web/queue/cron), ориентируйтесь на PSS для оценки реального вклада процесса. RSS хорош как индикатор «что-то происходит», но плохо подходит для распределения ответственности.

Утечка часто выглядит как рост RSS, но PSS при этом растёт гораздо медленнее (или почти не растёт), если увеличивается file-backed часть или шаренные маппинги.

В продакшене удобнее разбирать такие истории на изолированном стенде, где вы контролируете swap/overcommit и легко снимаете слепки. Для этого обычно берут отдельный инстанс на VDS.

Вывод /proc/PID/smaps_rollup как быстрый профиль памяти процесса

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Быстрая проверка: PID, status и «кто главный по памяти»

Начните с простого: найдите кандидатов по RSS и посмотрите, кто стабильно наверху. Это не «истина», но быстрый старт для дальнейшего разложения на anon/file/shmem.

ps -eo pid,comm,rss,etimes --sort=-rss | head

Затем загляните в /proc/PID/status — там удобные агрегаты, которые дают направление расследования.

pid=1234
cat /proc/$pid/status | egrep 'Name|VmRSS|VmHWM|VmSize|RssAnon|RssFile|RssShmem'

Ключевые поля:

  • VmRSS — текущий RSS
  • VmHWM — пик RSS (High Water Mark), полезно понять «это давно так» или «выросло недавно»
  • RssAnon — анонимные страницы (типично heap/stack/anon mmap)
  • RssFile — file-backed (библиотеки, mmap файлов)
  • RssShmem — shared memory

Если растёт RssAnon, чаще всего это heap/anon mmap. Если растёт RssFile, смотрим mmap файлов и поведение IO (иногда это норма для читающего сервиса).

smaps_rollup: самый полезный «одной командой»

Файл /proc/PID/smaps_rollup — агрегированная версия smaps. Это лучший старт для «профиля памяти» без парсинга сотен регионов.

pid=1234
cat /proc/$pid/smaps_rollup

Что смотреть в первую очередь:

  • Rss — общий RSS процесса
  • Pss — общий PSS процесса
  • Pss_Anon, Pss_File, Pss_Shmem — PSS по типам памяти
  • Anonymous — сколько анонимных страниц в маппингах
  • Swap — иногда «утечка» уходит в swap, а RSS выглядит терпимо

Если растёт именно Pss_Anon (и вместе с ним RssAnon в status) — это уже похоже на проблему внутри процесса: утечка, кэш аллокатора или активный mmap anon.

/proc/PID/smaps: когда нужно понять, какие регионы растут

/proc/PID/smaps — детализация по каждому VMA (виртуальному региону памяти). Там видно, какие маппинги дают вклад: heap, stack, анонимные регионы, memfd, конкретные файлы, shared libs.

Главная боль smaps — объём и необходимость суммирования. Но для расследования утечки важнее не «всё», а топ по PSS/RSS и их изменение во времени.

Ниже — практичный способ сгруппировать данные по «заголовку региона» и сложить PSS. Это не идеально (разные anon-регионы могут иметь одинаковую шапку), но часто даёт быстрый ответ.

pid=1234
awk '
  /^([0-9a-f]+-)+[0-9a-f]+/ {h=$0; next}
  /^Pss:/ {
    pss=$2
    key=h
    gsub(/^[0-9a-f-]++/, "", key)
    sum[key]+=pss
  }
  END {
    for (k in sum) printf "%10.0f kB  %s\n", sum[k], k
  }
' /proc/$pid/smaps | sort -nr | head -40

Как это читать:

  • строки вида [heap] — классический heap (malloc)
  • [stack], [stack:tid] — стеки потоков
  • [anon], пустой путь или безымянные регионы — это и есть «mmap anon»
  • пути к .so — библиотеки (обычно shared; PSS будет умеренным)
  • пути к файлам данных — mmap файлов или file-backed страницы
  • /memfd:... или (deleted) — частая находка при утечках через временные объекты или JIT

Поля внутри smaps, на которые реально смотреть

Для каждого региона полезны:

  • Size — размер виртуального региона (не равно RAM)
  • Rss — сколько страниц региона в RAM
  • Pss — пропорциональный вклад
  • Private_Clean / Private_Dirty — приватные страницы (не разделяются). Рост приватных — сильный сигнал утечки/кэша.
  • Shared_Clean / Shared_Dirty — шаренные страницы
  • Anonymous — сколько страниц в этом регионе анонимные

Если «растёт утечка», часто растут Private_Dirty и/или Anonymous в анонимных регионах или [heap].

pmap: быстрый обзор карты памяти и RSS по маппингам

pmap удобен, когда нужно быстро глазами увидеть «что за регионы» и их размеры. Он не заменяет smaps, но хорош как первый «рентген».

pid=1234
pmap -x $pid | head

Самое полезное — итоговая строка total kB и столбцы RSS/Dirty для крупных маппингов.

Если вы видите большие безымянные регионы с заметным Dirty, это типичная история про mmap anon. Если большие регионы привязаны к конкретному файлу, это file-backed память.

smem: когда нужно сравнить процессы «честно», с PSS

smem — инструмент для ответа на вопрос «какие процессы реально занимают память», а не «у кого большой RSS». Он умеет показывать PSS/USS.

Установка (пример):

apt-get update && apt-get install -y smem
dnf install -y smem

Топ процессов по PSS:

smem -tk | head -30

Топ по USS (Unique Set Size) — только приватные страницы процесса (условно «то, что можно освободить, убив процесс», без shared):

smem -uk | head -30

Это ключ к разбору истории «20 воркеров, каждый показывает 500 MB RSS»: shared библиотеки и shared memory раздувают RSS каждого, а PSS/USS обычно ставят всё на место.

Практический сценарий расследования (чек-лист)

Последовательность, которая чаще всего экономит время:

  1. Подтвердить тренд: растёт ли память монотонно и на каком горизонте (минуты/часы/дни). Снимите несколько точек.
  2. Сверить RSS с PSS: если PSS не растёт, «утечка» может быть иллюзией RSS.
  3. Разделить anon vs file: /proc/PID/status (RssAnon/RssFile) и smaps_rollup (Pss_Anon/Pss_File).
  4. Найти растущий тип маппинга: топ по PSS из smaps или быстрый взгляд через pmap -x.
  5. Сопоставить с поведением приложения: всплески трафика, очереди, фоновые джобы, загрузка файлов, компиляция шаблонов, кэши.
  6. Понять, это «не отдаёт аллокатор» или «реально накапливает»: перезапуск процесса освобождает? есть ли plateau? как ведёт себя после нагрузки?

Если вы крутите много сайтов/проектов и важно предсказуемое соседство по ресурсам, часто проще вынести тяжёлый сервис на отдельный VDS и уже там спокойно измерять PSS/USS и влияние swap.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Типовые «подводные камни»: почему память не возвращается

glibc malloc и арены (много потоков, много воркеров)

glibc malloc может держать арены на поток, фрагментировать кучу и не спешить возвращать память ядру. В результате:

  • после пика нагрузки RSS остаётся высоким;
  • внутренне приложение «свободно», но ОС этого не видит;
  • в smaps растёт [heap] и приватные dirty страницы.

Это не всегда утечка. Часто это цена за производительность. Но если на сервере тесно по RAM, становится проблемой: начинается swap, растут задержки, падает стабильность.

mmap anon: большие регионы, которые выглядят как утечка

Многие рантаймы и библиотеки берут память через mmap (не через brk/heap). Тогда рост будет не в [heap], а в безымянных регионах. В smaps это видно как большие anonymous-маппинги, а в smaps_rollup растёт Pss_Anon.

Частая причина — буферы, кэши, пул соединений, компрессия/архивация, обработка больших файлов, JIT, in-memory индексы. Отдельно обращайте внимание на /memfd:... и отметки (deleted): нередко там и лежит «пожиратель» RAM.

File-backed RSS: «кажется, что течёт», но это page cache

Если растёт RssFile/Pss_File, ищите mmap файлов и интенсивное чтение. Это может быть нормой: ядро держит часто используемые страницы в памяти. При давлении по RAM они будут вытеснены, а процесс продолжит работать (только медленнее).

Сравнение снимков smaps_rollup и status во времени для поиска растущих типов памяти

Снимки во времени: как увидеть, что именно растёт

Один снимок smaps_rollup полезен, но утечки — это динамика. Простейший «без агентов» способ — сохранить несколько слепков и сравнить.

pid=1234
ts=$(date +%F_%H%M%S)
cat /proc/$pid/smaps_rollup > /tmp/smaps_rollup_${pid}_${ts}.txt
cat /proc/$pid/status > /tmp/status_${pid}_${ts}.txt

Сравнение двух точек:

diff -u /tmp/smaps_rollup_1234_OLD.txt /tmp/smaps_rollup_1234_NEW.txt | sed -n '1,120p'

Если нужно сравнить распределение по регионам (heap/anon/file), делайте одинаковую агрегацию из smaps и сравнивайте топ-строки.

Когда пора лезть в приложение: профилирование аллокаций и рантайма

Если вы доказали, что растёт именно Pss_Anon и/или приватные dirty страницы (а не file cache), дальше два пути:

  • системный уровень: понять, heap это или anon mmap, и какой компонент потребляет;
  • уровень приложения: включить профилирование аллокаций и найти места выделения.

Для PHP-сервисов полезно параллельно исключить «память не причина, причина — зависшие воркеры». В этом помогает разбор медленных запросов через slowlog: как читать PHP-FPM slowlog в продакшене.

Если у вас JIT/VM и есть подозрение на рост через JIT-код или связанные буферы, посмотрите, как это обычно выглядит и какие метрики контролировать: PHP 8 JIT в продакшене: риски и диагностика.

Правильная последовательность: сначала PSS/anon/file на уровне ядра, затем выбор инструмента профилирования в рантайме. Иначе вы рискуете оптимизировать красивый график RSS, а не первопричину.

Мини-памятка: что использовать в какой момент

  • Нужно быстро понять, кто виноват в RAM на хосте: smem -tk (сортировка по PSS).
  • Нужно понять, из чего состоит память конкретного процесса: /proc/PID/smaps_rollup.
  • Нужно найти конкретные растущие маппинги: /proc/PID/smaps + агрегация, и/или pmap -x.
  • Нужно отделить утечку от «не отдаёт аллокатор»: сравнить несколько снимков, посмотреть рост приватных dirty страниц, проверить поведение после пика нагрузки (есть ли plateau), при необходимости — включить профилирование в рантайме/аллокаторе.

Диагностические вопросы, которые экономят часы

  • Растёт Pss или только Rss?
  • Растёт Pss_Anon или Pss_File?
  • Рост идёт в [heap] или в безымянных регионах (mmap anon)?
  • Есть ли корреляция с нагрузкой, кроном, конкретным типом запросов?
  • После снятия нагрузки память возвращается (полностью/частично/никогда)?
  • Сколько процессов/воркеров, и какой их реальный вклад по PSS/USS?

Если вы ответили на эти вопросы, то в большинстве случаев уже понятно, куда копать дальше: настройки воркеров и аллокатора, конкретные кэши, утечка объектов в коде или неконтролируемые mmap-регионы.

Заключение

В поиске «утечки» главное — не застрять на RSS. Комбинация smem (PSS по процессам), smaps_rollup (сводка по процессу), smaps (детализация по регионам) и pmap (быстрый обзор) даёт понятную картину: что именно растёт — heap, mmap anon, shared memory или file-backed страницы. А уже после этого имеет смысл включать профилировщики и исправлять первопричину в приложении.

Если сервис крутится в продакшене и важны предсказуемые ресурсы, проще всего жить с такими расследованиями на отдельной машине/инстансе: на VDS вы быстрее воспроизводите нагрузку, сравниваете слепки и контролируете swap/overcommit без сюрпризов от соседей.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...