Классическая ситуация. Заходишь на сервер, смотришь free -h, видишь, что свободной памяти почти нет. Открываешь top (или htop), сортируешь по RES - а там пусто, ни одного жирного процесса. Сумма по всем приложениям едва набирает половину занятого. Память есть, а кто её держит - непонятно. Вот тут новичок обычно зависает: начинает паниковать или сразу ребутить машину.
Не спеши. Очень часто память забирает не пользовательский процесс, а само ядро Linux - точнее, его внутренние кеши. И живут они в области, которую top вообще не показывает, потому что у ядра нет "процесса" в привычном смысле: нет PID, нет строки в списке задач. Это и есть тема урока: как устроена память ядра linux, что такое slab, и как с помощью утилиты slabtop за минуту понять, куда утекла RAM.
Сначала прикинь на пальцах, виновато ли вообще ядро. У free -h есть колонки used, free, buff/cache и available. Если used маленький, а буквально вся RAM в buff/cache - это, скорее всего, обычный кеш файлов (page cache), и available покажет, что памяти на самом деле полно. А вот если used большой, но в top нет соответствующих процессов - тогда копаем в ядро.
Этот навык нужен, когда: free показывает мало свободной памяти, а виновного процесса нет; когда сервер с кучей мелких файлов (почтовик в формате maildir, кеш веб-сервера, node_modules, миллионы временных файлов) начинает тормозить; когда после тяжёлого du, find или rsync по гигантскому дереву память будто прилипла. Разберёмся по шагам.

Что такое slab linux и зачем ядру свои кеши
Ядру постоянно нужны мелкие объекты одинакового размера: структура на каждый открытый файл, на каждый элемент пути в файловой системе, на каждый блок диска в памяти, на каждый сетевой сокет. Если под каждую такую структуру дёргать общий аллокатор страниц (страница - это 4 КБ на x86-64), получится дико расточительно и медленно: объект-то занимает 200 байт, а страница 4096. Девяносто пять процентов в мусор.
Чтобы не плодить отходы, в ядре есть slab-аллокатор. Идея простая, как лоток для яиц. Ядро берёт у системы несколько страниц подряд (это и есть "slab", плита) и нарезает их на одинаковые ячейки под объекты одного типа. Когда объект освобождается, ячейка не возвращается системе сразу, а остаётся в кеше - наготове под следующий такой же объект. Получаем пул заранее нарезанных ячеек: выделить и освободить почти мгновенно, без обращения к глобальному аллокатору и без блокировок на горячем пути.
Важно на 2026 год. Исторически в ядре было три аллокатора: SLAB, SLUB и SLOB. К нашему времени осталась только реализация SLUB: SLOB убрали в ядре 6.4, а старый SLAB пометили устаревшим в 6.5 и полностью вырезали в 6.8. Так что на любом современном дистрибутиве (ядро 6.x) под капотом именно SLUB. Для нас как для диагностов это почти ничего не меняет: те же кеши, тот же slabtop, тот же /proc/slabinfo. Меняются мелочи под капотом и набор отладочных ручек в /sys/kernel/slab. Имя "slab" как общий термин для технологии при этом осталось - не путай технологию (slab) и одну из её реализаций (SLAB, которой больше нет).
Каждый тип объекта живёт в своём именованном кеше. Главные, которые ты будешь встречать чаще всего:
- dentry - directory entry, кеш элементов путей. Каждый компонент пути (каждое имя файла или каталога, которое ядро трогало) оседает тут.
- inode_cache и ext4_inode_cache (или xfs_inode, btrfs_inode и т.п.) - кеш inode, метаданных файлов: размер, права, владелец, временные метки.
- buffer_head - служебные структуры для блоков блочного устройства.
- kmalloc-* - семейство кешей общего назначения под произвольные аллокации ядра (kmalloc-64, kmalloc-256, kmalloc-1k и т.д., размер в имени).
- TCP, sock_inode_cache, kmalloc-cg-* - сетевые структуры и аллокации, учтённые на cgroup (с cgroup v2 это норма по умолчанию).
- SReclaimable - то, что ядро может отдать обратно под давлением памяти. Сюда входят dentry и inode-кеши. Это по сути не "потеря" памяти, а полезный кеш: понадобится RAM приложению - ядро сожмёт эти кеши само, через механизм shrinker.
- SUnreclaim - то, что освободить нельзя, пока объекты реально используются. Если эта цифра неуклонно растёт днями и неделями при стабильной нагрузке - вот это уже повод заподозрить утечку в ядре или драйвере.
Первый инструмент - slabtop из пакета procps-ng. Это как top, только не для процессов, а для slab-кешей ядра в реальном времени. Запускаем с правами root:
Код: Выделить всё
sudo slabtop -s c
Код: Выделить всё
Active / Total Objects (% used) : 3120450 / 3340120 (93.4%)
Active / Total Slabs (% used) : 84210 / 84210 (100.0%)
Active / Total Caches (% used) : 142 / 198 (71.7%)
Active / Total Size (% used) : 980320.50K / 1042880.00K (94.0%)
Minimum / Average / Maximum Object : 0.01K / 0.31K / 16.00K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
1820000 1798200 98% 0.19K 43333 42 346664K dentry
640050 638900 99% 0.58K 23700 27 379200K ext4_inode_cache
210400 208100 98% 0.10K 5400 40 21600K buffer_head
...
- NAME - имя кеша. Видишь сверху dentry и ext4_inode_cache - значит, память съели кеши файловой системы.
- OBJS - всего объектов в кеше (и занятых, и пустых наготове).
- ACTIVE - сколько из них реально используется прямо сейчас.
- USE - процент использования, ACTIVE делить на OBJS. Если у dentry 98% - кеш плотно занят, мало "холодных" пустых ячеек.
- OBJ SIZE - размер одного объекта. У dentry это около 0.19K, мелочь. Но умножь на полтора миллиона штук - и набегают сотни мегабайт.
- SLABS - сколько "плит" (групп страниц) ушло под этот кеш.
- OBJ/SLAB - сколько объектов влезает в одну плиту.
- CACHE SIZE - итоговый размер кеша. Вот по нему мы и сортировали через -s c. Здесь dentry держит около 346 МБ, а inode-кеш около 379 МБ. Сложили - больше 700 МБ только на метаданных файлов.
Под капотом slabtop читает текстовый файл /proc/slabinfo. Можно глянуть и его напрямую (тоже от root):
Код: Выделить всё
sudo head -3 /proc/slabinfo
slabinfo - version: 2.1
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : ...
dentry 1820000 1820000 192 42 2 : ...
Код: Выделить всё
sudo sort -k2 -rn /proc/slabinfo | head -5
Код: Выделить всё
grep -E "Slab|SReclaimable|SUnreclaim|KernelStack|PageTables" /proc/meminfo
Slab: 1042880 kB
SReclaimable: 742300 kB
SUnreclaim: 300580 kB
KernelStack: 18432 kB
PageTables: 96420 kB
- Slab - суммарно всё, что забрал slab-аллокатор. Тут гигабайт. Это та память, которую top не показывал. Slab = SReclaimable + SUnreclaim.
- SReclaimable большой (742 МБ) - в основном dentry/inode. Это нормальный кеш, ядро отдаст его под нагрузкой. Паниковать не нужно. Кстати, именно SReclaimable учитывается в available памяти free, поэтому available может быть большим даже при огромном Slab.
- SUnreclaim - реально занятые ядром структуры. Норма зависит от железа и нагрузки, но резкий или линейный неконтролируемый рост этой цифры - тревога.
- KernelStack - память под стеки ядра, по одному на каждый поток (на x86-64 обычно 16 КБ на поток). Если она огромная - у тебя где-то форк-бомба или взрывное число тредов.
- PageTables - таблицы трансляции адресов. Раздувается у процессов с гигантскими картами памяти (большие БД, много shared-памяти, без HugePages).
Допустим, ты увидел гору в dentry/inode и хочешь проверить: это правда отдаваемый кеш или настоящая утечка? Есть ручка drop_caches. Она просит ядро отпустить чистые (не грязные) кеши. Значения такие:
- 1 - сбросить только page cache (кеш содержимого файлов).
- 2 - сбросить reclaimable slab, то есть dentry и inode-кеши.
- 3 - и то, и другое.
Код: Выделить всё
sync
echo 2 | sudo tee /proc/sys/vm/drop_caches
Очень важное предупреждение. drop_caches - это инструмент диагностики и тестов, а не средство "ускорить сервер". В интернете полно советов пихать его в cron - это вредный карго-культ. Ты выкидываешь полезный горячий кеш, и ядру придётся заново читать всё с диска: будет всплеск дисковых операций и просадка по latency сразу после сброса. На бою регулярно так делать не надо. Если dentry-кеш действительно мешает, правильнее тюнить vm.vfs_cache_pressure (повышение значения заставляет ядро агрессивнее освобождать dentry/inode), а не дёргать drop_caches по таймеру.
Уровень повыше: eBPF против утечек ядра (актуально на 2026)
slabtop отвечает на вопрос "сколько и в каком кеше", но не отвечает "кто это аллоцировал". Раньше тут начинались пляски с ftrace и kmemleak. Сегодня (2026) зрелый eBPF снимает вопрос почти полностью, и это уже стандарт диагностики, а не экзотика.
- slabratetop (из пакета bcc, в Debian/Ubuntu - bpfcc-tools) показывает скорость аллокаций по кешам в стиле top: какой кеш прямо сейчас активнее всего набивается. Если SUnreclaim растёт - запускаешь slabratetop и видишь, кто лидер по байтам в секунду.
- memleak (тоже bcc) - главный инструмент против утечек. Он инструментирует kmalloc/kfree, kmem_cache_alloc/kmem_cache_free и страничные аллокации, копит стеки вызовов и показывает те, что аллоцировали, но не освободили. То есть выдаёт прямо стек ядра, ведущий к утечке.
Код: Выделить всё
sudo slabratetop
sudo memleak -p $(pgrep -n myapp)
sudo memleak --kernel-threads-only 5
Код: Выделить всё
sudo bpftrace -e 'tracepoint:kmem:kmalloc { @bytes[kstack] = sum(args->bytes_alloc); }'
Типичные грабли и заблуждения
- "Память кончается - надо срочно чистить кеши." Нет. SReclaimable - это не утечка, ядро само освободит его, когда понадобится. Заполненный кеш - признак здоровой системы, а не больной. Смотри на available в free, а не на free.
- Запуск slabtop без sudo. /proc/slabinfo читается только root (права 0400 - это защита от утечки информации о раскладке памяти ядра, ввели как меру против эксплойтов). Без sudo получишь Permission denied или пустоту. Всегда sudo slabtop.
- Раздутый dentry после du/find/rsync по огромному дереву. Прошёлся по миллионам мелких файлов - ядро закешировало все элементы путей и inode. Это ожидаемо и отдаётся обратно. Не баг.
- Путать page cache и slab. Page cache (Cached в meminfo) - это содержимое файлов. Slab - это служебные структуры ядра. Разные вещи, разные значения drop_caches (1 против 2).
- Сразу кричать "утечка в ядре". Реальные утечки ядра/драйверов бывают, но редко. Признак - именно SUnreclaim или конкретный kmalloc-кеш, который растёт линейно во времени и НЕ сбрасывается через drop_caches 3. Сначала исключи нормальный кеш, потом запускай memleak, и только потом думай на драйвер.
- Искать аллокатор SLAB в /sys. На ядрах 6.8 и новее его нет, остался только SLUB. Старые гайды, где упоминается CONFIG_SLAB или /proc/slabinfo "в формате SLAB", частично устарели - формат полей при этом совместим.
Мини-лаба: сделай руками прямо сейчас
- Посмотри стартовое состояние: grep -E "Slab|SReclaimable|SUnreclaim" /proc/meminfo - запиши цифры.
- Прогрей dentry/inode-кеш: создай дерево из кучи мелких файлов и пройди по нему. Например: mkdir -p /tmp/slablab && for i in $(seq 1 50000); do touch /tmp/slablab/f$i; done, потом find /tmp/slablab -type f | wc -l и du -sh /tmp/slablab.
- Запусти sudo slabtop -s c -o и найди dentry с inode-кешем в топе. Посмотри, как выросли OBJS и CACHE SIZE.
- Сбрось reclaimable slab: sync && echo 2 | sudo tee /proc/sys/vm/drop_caches. Снова глянь meminfo - SReclaimable должен заметно упасть.
- Если стоит bcc: в одном терминале запусти sudo slabratetop, в другом снова прогони цикл с touch - увидишь всплеск аллокаций в dentry/inode-кешах в реальном времени.
- Убери за собой: rm -rf /tmp/slablab.
- free показывает мало свободной памяти, но в top нет жирных процессов. Какой первой командой проверишь, не ядро ли держит RAM, и по какой колонке (и каким флагом) отсортируешь slabtop?
- Чем SReclaimable отличается от SUnreclaim в /proc/meminfo и какая из этих цифр при бесконтрольном линейном росте намекает на утечку в ядре?
- Что произойдёт при echo 3 в drop_caches на нагруженном проде и почему нельзя ставить это в cron? Что тюнить вместо этого?
- slabtop показал, что раздут конкретный kmalloc-кеш, drop_caches не помогает. Каким инструментом 2026 года найдёшь стек ядра, который аллоцировал и не освободил память?
Если память занята, а виновного процесса нет - подозревай ядро. Открывай sudo slabtop -s c и смотри топ кешей: чаще всего наверху dentry и inode_cache, особенно на машинах с морем мелких файлов. Сверяйся с Slab / SReclaimable / SUnreclaim в /proc/meminfo: reclaimable - это здоровый кеш, ядро отдаст его само, он же учтён в available. drop_caches держи только для диагностики, и то аккуратно; для постоянной настройки есть vm.vfs_cache_pressure. А вот линейно растущий SUnreclaim, который не сбрасывается - редкий, но настоящий повод копать: запускай memleak или slabratetop из bcc и иди по стеку до конкретного драйвера или подсистемы ядра.