Память Linux: RSS, VSZ, page cache и миф о нехватке памяти

Рейтинг: 80.6% · 16 голосов
Подробный курс по диагностике и производительности Linux для админов и DevOps: с чего начать, методология USE, логи (journalctl, dmesg), процессы, CPU, память и OOM, диск и IO (iostat), сеть (ss, tcpdump), системные вызовы и strace, ltrace, perf, флеймграфы, ftrace, eBPF и bpftrace, lsof, мониторинг. Разбор каждого инструмента с чтением вывода.
Ответить
Аватара пользователя
Dmitry_SRE
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Память Linux: RSS, VSZ, page cache и миф о нехватке памяти

Сообщение Dmitry_SRE »

Оглавление курса (47)
  1. С чего начать диагностику Linux: методология вместо паники
  2. Первые 60 секунд: экспресс-диагностика нагруженного сервера
  3. Методологии диагностики: USE, RED и здравый смысл
  4. Логи systemd через journalctl: где искать причину
  5. Где лежат логи Linux: /var/log, dmesg и rsyslog
  6. Источники правды: load average, /proc и /sys
  7. Процессы Linux: ps, pstree и состояния процессов
  8. Интерактивный мониторинг: top и htop
  9. Метрики процессов во времени: pidstat
  10. Приоритеты и ограничения: nice, ionice, cgroups
  11. Сигналы и зависшие процессы: kill, и что делать с D-state
  12. Загрузка CPU: user, system, iowait, steal и контекст-свитчи
  13. Диагностика CPU по ядрам: vmstat и mpstat
  14. Профилирование CPU: perf top и поиск пожирателя
  15. Частоты, троттлинг и NUMA: turbostat и numastat
  16. Память Linux: RSS, VSZ, page cache и миф о нехватке памяти (вы здесь)
  17. Сколько памяти занято: free, /proc/meminfo и vmstat
  18. Поиск утечек и пожирателей памяти: smem, pmap, smaps
  19. Swap и подкачка: swapon, swappiness, когда своп - это боль
  20. OOM killer: кто и за что убил процесс
  21. Память ядра и slab: slabtop и куда уходит RAM
  22. Подсистема ввода-вывода: путь запроса от приложения до диска
  23. Диагностика диска: iostat и чтение await, %util
  24. Кто грузит диск: iotop и атрибуция io процессам
  25. Файловые системы: df, du, иноды и куда делось место
  26. Глубокая диагностика блочного слоя: blktrace и biolatency
  27. Кеш страниц и грязные данные: dirty pages, fsync, drop_caches
  28. Сетевой стек Linux: путь пакета и где возникают задержки
  29. Сокеты и соединения: ss и netstat
  30. Захват трафика: tcpdump для диагностики сети
  31. Задержки и потери в сети: ping, mtr, диагностика латентности
  32. Сеть как источник проблем: nftables, conntrack, дропы
  33. Что такое системный вызов и зачем его трассировать
  34. strace: трассировка системных вызовов на практике
  35. strace в бою: почему программа висит, падает или тормозит
  36. ltrace: трассировка вызовов библиотек
  37. Накладные расходы трассировки и безопасные альтернативы
  38. perf: универсальный профайлер Linux
  39. Флеймграфы: визуализация профиля производительности
  40. Трассировка ядра: ftrace и trace-cmd
  41. eBPF: революция в наблюдаемости Linux
  42. Инструменты eBPF на практике: BCC и bpftrace
  43. lsof: открытые файлы, дескрипторы, кто держит файл и порт
  44. Разбор кейса: приложение тормозит - пошаговая диагностика
  45. Непрерывный мониторинг: sar, node_exporter, Grafana, Zabbix
  46. Диагностика в контейнерах: cgroups, лимиты, Docker и Kubernetes
  47. Карта инструментов и сквозной разбор инцидента производительности
"У меня всего 200 МБ свободно, сервер сейчас умрёт!" - спокойно, не умрёт

Едва ли не самая частая паника новичка звучит так: запустил , увидел, что free всего пара сотен мегабайт из 16 ГБ, и побежал перезагружать сервер или докупать память. А память при этом не кончилась и кончаться не собиралась. Это классическое заблуждение, и сегодня мы разберём, как на самом деле устроена операционная память Linux и почему "linux в оперативной памяти" почти всегда показывает, что свободного якобы мало.

Тема денежная и по делу: пока ты не понимаешь разницу между VSZ, RSS и available, ты не можешь ни найти утечку, ни оценить, сколько процессов влезет на хост, ни ответить на вопрос "а нам точно нужно больше памяти". После этого урока ты будешь читать вывод , ,

Код: Выделить всё

/proc/meminfo
и современные метрики давления памяти осознанно, а не на эмоциях. Всё актуально на 2026 год.

Изображение

Виртуальная память Linux: VSZ против RSS на пальцах

Каждый процесс живёт в своём виртуальном адресном пространстве. Виртуальная память Linux - это как карта города: процесс видит огромную карту адресов, но реально застроены (заняты физической RAM) только некоторые кварталы. Остальное - просто зарезервированные участки, библиотеки, которые ещё не подгрузились, и страницы, к которым процесс ещё не прикасался. Ядро выделяет физическую страницу лениво, в момент первого обращения - это называется page fault и demand paging. Поэтому процесс может "выделить" 10 ГБ через malloc, но пока он туда не записал, физическая RAM не тратится вообще.

Отсюда две метрики, которые путают чаще всего:
  • VSZ (virtual size) - весь размер виртуального адресного пространства процесса. Сюда входит код, данные, разделяемые библиотеки, mmap-нутые файлы и даже то, что зарезервировано, но физически не выделено. VSZ может быть гигантским и почти ничего не значит сам по себе.
  • RSS (resident set size) - сколько физических страниц RAM процесс реально держит прямо сейчас. Это уже ближе к правде, но есть подвох: если 50 процессов используют одну и ту же библиотеку libc, её страницы посчитаются в RSS у каждого. Сложишь RSS всех процессов - получишь больше, чем физической памяти в машине. Это нормально, RSS просто не умеет делить общие страницы.
Поэтому есть третья, честная метрика - PSS (proportional set size). Она берёт разделяемые страницы и делит их поровну между теми, кто их использует. Если страница libc общая на 10 процессов, каждому в PSS запишется 1/10. Сумма PSS по всем процессам уже корректно отражает реальное потребление. Есть и четвёртая - USS (unique set size), это сугубо приватная память процесса (Private_Clean + Private_Dirty); USS - это примерно столько RAM освободится, если процесс убить. Смотрят их через

Код: Выделить всё

/proc/PID/smaps_rollup
или утилитой .

Page cache: почему свободной памяти "мало" и это правильно

Теперь главное про page cache, из-за которого и рождается миф. Когда ты читаешь файл с диска, ядро кладёт его содержимое в RAM - в страничный кеш (page cache). В следующий раз тот же файл отдаётся из памяти, без обращения к диску. Диск медленный, RAM быстрая, поэтому ядро жадно набивает кешем всю свободную память.

Логика ядра простая: пустая RAM - это потраченные впустую деньги. Лучше держать там кеш файлов, потому что его можно мгновенно выбросить (reclaim), как только приложению понадобится память. То есть buff/cache - это не "занято навсегда", это "занято, но отдадим по первому требованию". Тонкость: чистый (clean) кеш выбрасывается мгновенно, а грязный (dirty, ещё не записанный на диск) сперва надо сбросить на диск - его текущий объём виден как Dirty в

Код: Выделить всё

/proc/meminfo
.

Вот почему показывает мало free даже на здоровой системе. Память забита кешем, и это не проблема, а фича. Паниковать из-за низкого free - всё равно что паниковать из-за того, что холодильник полный: его можно освободить за секунду, если надо.

Память бывает двух видов, и это важно для понимания, что выбрасывается, а что нет:
  • file-backed (привязана к файлу на диске) - тот самый page cache и код программ. Её можно просто выкинуть из RAM: если понадобится снова, перечитается с диска.
  • anonymous (анонимная) - куча и стек процессов, данные, у которых нет файла на диске. Её выкинуть нельзя, можно только выгрузить в swap. Именно рост anonymous-памяти обычно и есть утечка.
Отдельно есть shared-память (tmpfs, разделяемые сегменты POSIX/SysV), в это колонка shared. Важно: всё, что лежит в tmpfs (а это, например, /dev/shm и в современных дистрибутивах /tmp, /run), - это анонимная по природе память, она НЕ скидывается на диск, а при нехватке уходит только в swap. Большой /dev/shm под завязку может неожиданно довести до OOM.

Практика: читаем free, /proc/meminfo и ps по полям

Начнём с :

Код: Выделить всё

free -h
               total        used        free      shared  buff/cache   available
Mem:            15Gi       3.2Gi       250Mi       180Mi        12Gi        11Gi
Swap:          2.0Gi          0B       2.0Gi
Разбираем по колонкам, это ключевой навык:
  • total - всего физической RAM, 15 Gi.
  • used - реально занято процессами (в современном procps это total минус free минус buff/cache, то есть анонимная память приложений плюс неотдаваемые ядерные структуры), 3.2 Gi.
  • free - совсем пустая, ни на что не отданная память. Здесь всего 250 Mi - и это та цифра, на которую НЕ надо смотреть.
  • buff/cache - 12 Gi отданы под кеш файлов, буферы и реклеймируемый slab. Это и есть тот резерв, который ядро вернёт под нагрузку.
  • available - вот честная метрика. 11 Gi - столько реально можно отдать новым приложениям без свопа, потому что ядро посчитало free плюс ту часть кеша, которую безопасно выбросить.
Запомни одно правило: смотри на available, а не на free. free near zero - норма, available near zero - вот это уже тревога. (Историческая сноска: в очень старом procps была отдельная строка -/+ buffers/cache; современный её убрал и показывает available вместо неё, так что старые гайды с этой строкой - устаревшие.)

Откуда берётся available, видно в

Код: Выделить всё

/proc/meminfo
:

Код: Выделить всё

grep -E 'MemTotal|MemFree|MemAvailable|Buffers|Cached|SReclaimable|Dirty|SwapTotal' /proc/meminfo
MemTotal:       16108112 kB
MemFree:          256120 kB
MemAvailable:   11503204 kB
Buffers:          204800 kB
Cached:         11890048 kB
SReclaimable:     410200 kB
Dirty:             18204 kB
SwapTotal:       2097148 kB
MemAvailable ядро считает само в функции si_mem_available(): берёт MemFree, прибавляет реклеймируемую часть page cache (file LRU) и примерно половину реклеймируемого slab (SReclaimable), и вычитает low watermark-резерв по всем зонам, который нужен ядру для работы. Оно сознательно считает консервативно: half-эвристика на slab и file-LRU нужна потому, что часть кеша занята используемыми сейчас элементами и реально не освободится. Поэтому MemAvailable - это не просто free плюс cache, а аккуратная оценка с поправками. Изредка он бывает даже меньше MemFree (если в системе много зарезервированных страниц и мало реклеймируемого кеша) - так и задумано, это не баг.

Теперь по процессам - :

Код: Выделить всё

ps -o pid,vsz,rss,comm -C nginx
  PID    VSZ   RSS COMMAND
 1042 142560  9200 nginx
 1043 142560  8800 nginx
VSZ и RSS тут в килобайтах. Видишь: VSZ под 140 МБ, а RSS всего ~9 МБ. Реально процесс держит в RAM 9 МБ, остальное - виртуальная разметка. Не пугайся большого VSZ, особенно у JVM, Go или баз данных - они резервируют адресное пространство щедро (Go-рантайм и многопоточные glibc-приложения с аренами malloc легко показывают десятки гигабайт VSZ при крошечном RSS).

Чтобы увидеть честную PSS-картину по процессу:

Код: Выделить всё

sudo grep -E '^(Rss|Pss|Shared|Private|Swap)' /proc/1042/smaps_rollup
Rss:                9200 kB
Pss:                5100 kB
Shared_Clean:       6200 kB
Shared_Dirty:          0 kB
Private_Clean:       800 kB
Private_Dirty:      2200 kB
Swap:                  0 kB
Здесь Rss 9200, но Pss всего 5100 - потому что часть страниц общие (Shared_Clean, разделяемые библиотеки), и в Pss они учтены лишь долей. Private_Dirty (2200 kB) - это приватные изменённые страницы, та память, которую процесс реально "владеет" единолично и которую нельзя ни с кем разделить. Если ищешь утечку - следи именно за ростом Private_Dirty и Pss; ещё полезно смотреть строку Swap в smaps_rollup, чтобы видеть, сколько страниц процесса уже выселено в своп.

Удобный обзор по PSS/USS сразу по всем процессам даёт :

Код: Выделить всё

sudo smem -t -k -c "pid user command pss uss"
Колонка PSS суммируется внизу корректно (без двойного учёта общих страниц), в отличие от суммы RSS.

Современный взгляд 2026: PSI, cgroup v2 и проактивный OOM

Метрики выше отвечают на вопрос "сколько занято". Но в 2026 важнее вопрос "система СТРАДАЕТ от нехватки памяти прямо сейчас или нет". На него отвечает PSI (Pressure Stall Information) - это уже стандарт в ядрах с CONFIG_PSI=y:

Код: Выделить всё

cat /proc/pressure/memory
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=12.30 avg300=4.10 total=98234112
Читается так: some - доля времени (в процентах за окна 10/60/300 секунд), когда хотя бы одна задача тормозила в ожидании памяти; full - когда вообще все непростаивающие задачи стояли из-за памяти (это уже почти полная остановка полезной работы). Ориентиры: some avg10 ниже ~5% - здорово; устойчиво выше 20% - скоро прилетит OOM; full avg10 стабильно больше нуля - система буксует в reclaim/свопе, разбирайся немедленно. PSI ловит проблему раньше, чем кончится available, потому что показывает не объём, а боль.

Почти все современные дистрибутивы используют cgroup v2 по умолчанию (актуально на 2026). Память контейнера/юнита смотрят не в общесистемном free, а в его cgroup:

Код: Выделить всё

cat /sys/fs/cgroup/system.slice/myapp.service/memory.current
cat /sys/fs/cgroup/system.slice/myapp.service/memory.stat
cat /sys/fs/cgroup/system.slice/myapp.service/memory.pressure

Код: Выделить всё

memory.current
- сколько байт сейчас занимает группа (включая её page cache!); в

Код: Выделить всё

memory.stat
разбивка по anon, file, slab и прочему;

Код: Выделить всё

memory.pressure
- тот же PSI, но только для этой группы. Лимиты задаются через

Код: Выделить всё

memory.max
(жёсткий потолок, при превышении - reclaim, потом OOM внутри группы) и

Код: Выделить всё

memory.high
(мягкий, тормозит группу через throttling, не убивая). Это критично для контейнеров: твой под может прекрасно жить в общем free, но упереться в свой

Код: Выделить всё

memory.max
и получить локальный OOM-kill - в это видно как "Memory cgroup out of memory".

Когда памяти реально не хватает, в дело вступает OOM killer. Современная практика - не ждать жёсткого ядерного OOM (он часто срабатывает поздно, когда система уже в свап-спирали), а ставить проактивный убийца на PSI:

Код: Выделить всё

systemd-oomd
(штатный в systemd) или

Код: Выделить всё

earlyoom
. Они смотрят на memory.pressure и swap и гасят жертву мягко, до зависания. Ранние сигналы беды видны в

Код: Выделить всё

/proc/vmstat
по растущим счётчикам allocstall (прямой reclaim) и pgscan/pgsteal. И ещё одно свежее: в современных ядрах включён MGLRU (Multi-Gen LRU) - переработанный алгоритм вытеснения страниц, он эффективнее старого active/inactive LRU и снижает лишний своп под давлением; для тебя это означает, что под нагрузкой система реклеймит точнее, но вся диагностика выше (PSI, available, swap-in/out) остаётся ровно такой же.

Типичные грабли и заблуждения
  • "free мало - надо срочно докупать память." Нет. Смотри available и /proc/pressure/memory. Если available большой и PSI около нуля, всё в порядке.
  • Код: Выделить всё

    echo 3 > /proc/sys/vm/drop_caches
    для "освобождения" памяти.[/b] Бесполезно и вредно в проде: ты выбрасываешь горячий кеш, после чего система лезет на диск и тормозит, пока кеш не прогреется заново. Делать это стоит только для воспроизводимых бенчмарков.
  • Сложил RSS всех процессов и получил больше, чем total. Это не баг, RSS дублирует общие страницы. Для реального учёта бери PSS (smem или smaps_rollup).
  • Большой VSZ = утечка. Нет. VSZ почти ничего не говорит. Утечку ищи по растущему RSS/Pss и anonymous-памяти.
  • "Swap занят, значит памяти не хватает." Не обязательно. Ядро могло выгрузить давно не используемые анонимные страницы в swap, чтобы освободить RAM под кеш. Тревога - это активный swap-in/swap-out (колонки si/so в

    Код: Выделить всё

    vmstat 1
    ) и ненулевой PSI, а не сам факт занятого свопа.
  • "vm.swappiness=0 выключает своп и ускоряет систему." Нет, 0 лишь максимально оттягивает выселение анонимных страниц, но не запрещает его при реальной нехватке; полностью без свопа система быстрее зовёт OOM killer. Своп - это страховка, а не зло.
  • Считать память контейнера по общесистемному free. В мире cgroup v2 это ошибка - смотри memory.current и memory.max самой группы.
Мини-лаба: проверь руками прямо сейчас
  • Запусти и найди глазами free и available. Прочувствуй разницу в цифрах.
  • Прогрей кеш:

    Код: Выделить всё

    cat большой_файл > /dev/null
    , потом снова . Увидишь, как buff/cache подрос, а available почти не изменился - кеш не "съел" доступную память.
  • Найди топ процессов по памяти:

    Код: Выделить всё

    ps -eo pid,rss,vsz,comm --sort=-rss | head
    . Сравни VSZ и RSS у самого жирного.
  • Возьми его PID и глянь

    Код: Выделить всё

    sudo cat /proc/PID/smaps_rollup
    - сравни Rss, Pss и Private_Dirty.
  • Поставь и запусти

    Код: Выделить всё

    sudo smem -t -k -c "pid command pss uss"
    , сравни сумму PSS с used из free.
  • Запусти

    Код: Выделить всё

    vmstat 1 5
    и посмотри на колонки si/so (swap-in/out) и на free/buff/cache в динамике.
  • Глянь давление:

    Код: Выделить всё

    cat /proc/pressure/memory
    . На спокойной системе все avg должны быть около нуля.
Под Astra Linux и RED OS всё это работает один в один - ядро то же, procps тот же, cgroup v2 и PSI на месте.

Контрольные вопросы
  • На какую колонку нужно смотреть, чтобы понять, сколько памяти реально доступно новым приложениям, и почему не на free?
  • Чем PSS отличается от RSS и в какой ситуации сумма RSS по всем процессам обманет тебя? Что покажет USS?
  • Почему ядро намеренно забивает почти всю свободную RAM под page cache и плохо ли это?
  • Какую память (anonymous или file-backed) нельзя просто выбросить из RAM и куда она уходит при нехватке памяти?
  • Что показывает /proc/pressure/memory и чем строка full опаснее строки some?
Что запомнить

Свободная память в Linux - это зря потраченная память, поэтому ядро держит в ней page cache. free показывает мало не потому, что памяти нет, а потому, что она работает на тебя. Честная метрика свободного - available (она же MemAvailable), а не free. VSZ - виртуальная разметка, ему верить нельзя; RSS - резидентная память, но дублирует общие страницы; PSS - честный учёт с делением разделяемого, USS - чисто приватное процесса. Утечку ищи по растущим anonymous и Private_Dirty, а не по VSZ. В 2026 для оценки боли системы смотри PSI (/proc/pressure/memory), а для контейнеров - memory.current/memory.max в cgroup v2; проактивный убийца (systemd-oomd / earlyoom) спасает раньше ядерного OOM. И не дёргай drop_caches на проде.
👍3 ❤️ 🔥1 😄 🤔2
Аватара пользователя
moonseed
Сообщения: 1
Зарегистрирован: 22 май 2026, 00:19

Re: Память Linux: RSS, VSZ, page cache и миф о нехватке памяти

Сообщение moonseed »

Блин, я же реально на прошлой неделе перезагрузил прод из-за low free, а там available было 9 гигов. Спасибо, теперь хоть понятно куда смотреть. PSI вообще не знал, поставил мониторить some avg10.
👍3 ❤️1 🔥 😄 🤔
Аватара пользователя
python_ninja
Сообщения: 1
Зарегистрирован: 22 май 2026, 17:24

Re: Память Linux: RSS, VSZ, page cache и миф о нехватке памяти

Сообщение python_ninja »

А подскажите, для поиска утечки в python-сервисе мне ориентироваться на rss или на pss из smaps_rollup? У меня там воркеры форкаются, rss суммарно больше чем вся память. И мы в k8s, так что я ещё в memory.current контейнера теперь смотрю, а не в общий free.
👍 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
Частоты, троттлинг и NUMA: turbostat и numastat
Следующая глава →
Сколько памяти занято: free, /proc/meminfo и vmstat

Все главы курса «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Поделиться темой: ✈ Telegram VK
Похожие запросы: как посмотреть сколько памяти занято в linuxlsof кто держит файл и порт в linuxperf top как найти что грузит процессорчто такое системный вызов и зачем трассироватьhtop и top как читать и в чем разницаoom killer кто убил процесс linux как узнать

Вернуться в «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость