Едва ли не самая частая паника новичка звучит так: запустил
Код: Выделить всё
free -hТема денежная и по делу: пока ты не понимаешь разницу между VSZ, RSS и available, ты не можешь ни найти утечку, ни оценить, сколько процессов влезет на хост, ни ответить на вопрос "а нам точно нужно больше памяти". После этого урока ты будешь читать вывод
Код: Выделить всё
freeКод: Выделить всё
psКод: Выделить всё
/proc/meminfo
Виртуальная память 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 просто не умеет делить общие страницы.
Код: Выделить всё
/proc/PID/smaps_rollupКод: Выделить всё
smemPage cache: почему свободной памяти "мало" и это правильно
Теперь главное про page cache, из-за которого и рождается миф. Когда ты читаешь файл с диска, ядро кладёт его содержимое в RAM - в страничный кеш (page cache). В следующий раз тот же файл отдаётся из памяти, без обращения к диску. Диск медленный, RAM быстрая, поэтому ядро жадно набивает кешем всю свободную память.
Логика ядра простая: пустая RAM - это потраченные впустую деньги. Лучше держать там кеш файлов, потому что его можно мгновенно выбросить (reclaim), как только приложению понадобится память. То есть buff/cache - это не "занято навсегда", это "занято, но отдадим по первому требованию". Тонкость: чистый (clean) кеш выбрасывается мгновенно, а грязный (dirty, ещё не записанный на диск) сперва надо сбросить на диск - его текущий объём виден как Dirty в
Код: Выделить всё
/proc/meminfoВот почему
Код: Выделить всё
freeПамять бывает двух видов, и это важно для понимания, что выбрасывается, а что нет:
- file-backed (привязана к файлу на диске) - тот самый page cache и код программ. Её можно просто выкинуть из RAM: если понадобится снова, перечитается с диска.
- anonymous (анонимная) - куча и стек процессов, данные, у которых нет файла на диске. Её выкинуть нельзя, можно только выгрузить в swap. Именно рост anonymous-памяти обычно и есть утечка.
Код: Выделить всё
freeПрактика: читаем free, /proc/meminfo и ps по полям
Начнём с
Код: Выделить всё
freeКод: Выделить всё
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 плюс ту часть кеша, которую безопасно выбросить.
Код: Выделить всё
freeОткуда берётся 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Теперь по процессам -
Код: Выделить всё
psКод: Выделить всё
ps -o pid,vsz,rss,comm -C nginx
PID VSZ RSS COMMAND
1042 142560 9200 nginx
1043 142560 8800 nginxЧтобы увидеть честную 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Удобный обзор по PSS/USS сразу по всем процессам даёт
Код: Выделить всё
smemКод: Выделить всё
sudo smem -t -k -c "pid user command pss uss"Современный взгляд 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Почти все современные дистрибутивы используют 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Код: Выделить всё
memory.statКод: Выделить всё
memory.pressureКод: Выделить всё
memory.maxКод: Выделить всё
memory.highКод: Выделить всё
memory.maxКод: Выделить всё
dmesgКогда памяти реально не хватает, в дело вступает OOM killer. Современная практика - не ждать жёсткого ядерного OOM (он часто срабатывает поздно, когда система уже в свап-спирали), а ставить проактивный убийца на PSI:
Код: Выделить всё
systemd-oomdКод: Выделить всё
earlyoomКод: Выделить всё
/proc/vmstatТипичные грабли и заблуждения
- "free мало - надо срочно докупать память." Нет. Смотри available и /proc/pressure/memory. Если available большой и PSI около нуля, всё в порядке.
- для "освобождения" памяти.[/b] Бесполезно и вредно в проде: ты выбрасываешь горячий кеш, после чего система лезет на диск и тормозит, пока кеш не прогреется заново. Делать это стоит только для воспроизводимых бенчмарков.
Код: Выделить всё
echo 3 > /proc/sys/vm/drop_caches - Сложил RSS всех процессов и получил больше, чем total. Это не баг, RSS дублирует общие страницы. Для реального учёта бери PSS (smem или smaps_rollup).
- Большой VSZ = утечка. Нет. VSZ почти ничего не говорит. Утечку ищи по растущему RSS/Pss и anonymous-памяти.
- "Swap занят, значит памяти не хватает." Не обязательно. Ядро могло выгрузить давно не используемые анонимные страницы в swap, чтобы освободить RAM под кеш. Тревога - это активный swap-in/swap-out (колонки si/so в ) и ненулевой PSI, а не сам факт занятого свопа.
Код: Выделить всё
vmstat 1 - "vm.swappiness=0 выключает своп и ускоряет систему." Нет, 0 лишь максимально оттягивает выселение анонимных страниц, но не запрещает его при реальной нехватке; полностью без свопа система быстрее зовёт OOM killer. Своп - это страховка, а не зло.
- Считать память контейнера по общесистемному free. В мире cgroup v2 это ошибка - смотри memory.current и memory.max самой группы.
- Запусти и найди глазами free и available. Прочувствуй разницу в цифрах.
Код: Выделить всё
free -h - Прогрей кеш: , потом снова
Код: Выделить всё
cat большой_файл > /dev/null. Увидишь, как buff/cache подрос, а available почти не изменился - кеш не "съел" доступную память.Код: Выделить всё
free -h - Найди топ процессов по памяти: . Сравни VSZ и RSS у самого жирного.
Код: Выделить всё
ps -eo pid,rss,vsz,comm --sort=-rss | head - Возьми его PID и глянь - сравни Rss, Pss и Private_Dirty.
Код: Выделить всё
sudo cat /proc/PID/smaps_rollup - Поставь и запусти , сравни сумму PSS с used из free.
Код: Выделить всё
sudo smem -t -k -c "pid command pss uss" - Запусти и посмотри на колонки si/so (swap-in/out) и на free/buff/cache в динамике.
Код: Выделить всё
vmstat 1 5 - Глянь давление: . На спокойной системе все avg должны быть около нуля.
Код: Выделить всё
cat /proc/pressure/memory
Контрольные вопросы
- На какую колонку нужно смотреть, чтобы понять, сколько памяти реально доступно новым приложениям, и почему не на free?
Код: Выделить всё
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 на проде.