Linux: 10‑хвилинний метод, щоб знайти, що насправді зʼїдає вашу оперативну памʼять

Було корисно?

Вам дзвонять на пейдж: «Хост закінчився памʼяттю». Ви заходите по SSH, запускаєте free -h, і воно каже, що памʼять «used». Дуже корисно. Наче пожежний датчик, який тільки каже «ДИМ».

Трюк у тому, щоб перестати сприймати ОЗП як одну купу. Linux розбиває її на відсіки, що мають оперативне значення: анонімна памʼять (процеси), файловий кеш (швидкий ввід/вивід), slab ядерних кешів (метадані) і обмеження, накладені cgroups. За десять хвилин зазвичай можна ідентифікувати справжнього «їдця» — і вибрати правильне рішення замість класичного підходу: перезапустити сервер і молитися.

Швидкий плейбук діагностики

Це порядок дій, який не дозволяє вам гнатись за примарами. Ви найперше шукаєте найбільший відсік, потім звужуєте до винуватця, а наприкінці валідовуєте даними з власного обліку ядра.

Перше: це реальний тиск памʼяті чи лише файловий кеш?

  • Перевірте free -h і зосередьтесь на available, а не на used.
  • Перевірте активність swap (vmstat / sar) і великі page faultʼи, якщо вони у вас є.

Друге: чи ядро під тиском памʼяті (ризик OOM) і чому?

  • Подивіться dmesg на предмет логів OOM killer і яка cgroup спрацювала.
  • Перевірте /proc/meminfo для розподілу anon vs file vs slab.

Третє: ідентифікуйте головних споживачів за правильною метрикою

  • Почніть з RSS на процес, щоб знайти очевидних «жерців» (ps).
  • Потім використайте PSS, коли спільна памʼять робить RSS оманливим (smem або /proc/*/smaps_rollup).
  • Якщо задіяні контейнери — перевірте cgroups першими; «top на хості» — не інструмент обліку памʼяті контейнера.

Четверте: якщо процеси не пояснюють ситуацію, підозрівайте памʼять ядра

  • Зростання slab: slabtop, /proc/slabinfo, рядок Slab у meminfo.
  • Стек ядра, таблиці сторінок і невідновлювана памʼять можуть тихо вивести хост з ладу.

Пʼяте: підтвердьте шлях виправлення перш ніж робити драматичні дії

  • Вбивайте/перезапускайте лише винуватця, а не весь сервер.
  • Встановіть розумні ліміти (systemd/Kubernetes) після того, як зрозумієте steady‑state використання.
  • Виправляйте витоки з доказами: криві росту, а не відчуття.

Ментальна модель: що насправді означає «used RAM»

Linux агресивно використовує ОЗП, бо проста невикористана памʼять — втрачені можливості. Воно заповнить памʼять файловим кешем, dentries, inode‑кешем та іншими помічниками продуктивності. Це не витік; це ядро, яке виконує свою роботу.

Операційне питання не «Чому used високий?», а «Чи система позбавлена відновлюваної памʼяті?». Тому free показує оцінку available. «Available» приблизно означає: якщо зараз стартувати нове навантаження, скільки ядро може звільнити без катастрофи?

Памʼять починає реально тиснути, коли:

  • swap активно використовується і особливо коли швидкості swap‑in/out немалі.
  • прямий reclaim гальмує роботу: стрибки латентності, CPU у ядрі, kswapd зайнятий.
  • зʼявляється OOM killer, на рівні хоста або всередині cgroup (контейнери люблять тихо вмирати).
  • зростає slab або невідновлювана памʼять, залишаючи менше відновлюваного простору.

Ось розподіл, який варто тримати в голові:

  • Анонімна памʼять (AnonPages): купи, стек, JIT‑памʼять, malloc. Це те, що споживають «додатки».
  • Файлова памʼять (Cached): файловий кеш. Зазвичай відновлювана при потребі.
  • Slab (SReclaimable + SUnreclaim): кеші/метадані ядра. Частково відновлювані.
  • Committed memory: обіцянки. Не всі обіцянки стають боргом, але слід слідкувати за overcommit.
  • cgroups: обмеження. Ви можете мати багато фізичної памʼяті на хості й усе одно отримати OOM усередині контейнера.

Одна парафразована ідея від легенди надійності: парафразована ідея — John Allspaw стверджував, що інциденти в продакшені часто — це «звична робота», яка зіштовхується з припущеннями; сторінки памʼяті ідеально вписуються в цю схему.

Жарт №1: Усунення проблем з памʼяттю — як дієта: числа реальні, але ярлики обманюють вас.

10‑хвилинний метод (завдання з командами)

Нижче — практичні завдання, які можна виконати на будь‑якому Linux‑сервері (bare metal, VM, хост контейнерів). Кожне завдання містить: команду, що означає її вивід, і яке рішення приймати.

Завдання 1: дивіться реальність через free

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            31Gi        27Gi       1.2Gi       512Mi       2.8Gi       2.9Gi
Swap:          8.0Gi       2.1Gi       5.9Gi

Що це означає: Ключове число — available. Тут воно ~2.9Gi, що не комфортно при різкому сплеску навантаження. Swap уже використовується (2.1Gi), що натякає на реальний тиск.

Рішення: Якщо available низький і swap використовується або росте — продовжуйте діагностику. Якщо available у нормі і swap спокійний, то «used RAM» — ймовірно кеш, і проблема може бути в іншому (диск чи CPU).

Завдання 2: перевірте paging і поведінку reclaim через vmstat

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0 2211840 923456  65536 911872   12   40    15    20  420  880 12  6 78  4  0
 1  0 2211840 905216  65536 918528    0   16     0     8  410  860 11  6 79  4  0
 3  0 2211840 892928  65536 924160    0    0     0     0  450  910 14  7 76  3  0
 4  0 2211840 876544  65536 931072    0   24     0    12  470  980 17  8 72  3  0
 2  0 2211840 868352  65536 936960    0    8     0     4  440  900 13  6 77  4  0

Що це означає: si/so — swap‑in/out за секунду. Нульові значення протягом часу означають, що ядро активно переставляє сторінки. Також дивіться wa (I/O wait) і b (blocked processes).

Рішення: Якщо swap‑out триває — у вас тиск памʼяті. Наступний крок — визначити, який відсік росте: процеси, кеш, slab чи cgroups.

Завдання 3: прочитайте «сировину правди»: /proc/meminfo

cr0x@server:~$ egrep 'MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|AnonPages|Mapped|Shmem|Slab|SReclaimable|SUnreclaim|KernelStack|PageTables' /proc/meminfo
MemTotal:       32949044 kB
MemFree:         842112 kB
MemAvailable:   3026112 kB
Buffers:          65536 kB
Cached:          956812 kB
SwapTotal:      8388604 kB
SwapFree:       6193152 kB
AnonPages:     25801120 kB
Mapped:          612340 kB
Shmem:           524288 kB
Slab:           1523400 kB
SReclaimable:    812000 kB
SUnreclaim:      711400 kB
KernelStack:      112000 kB
PageTables:       184000 kB

Що це означає: AnonPages величезний: процеси — головний споживач. Cached відносно малий, отже це не «лише файловий кеш». Slab немалий; зверніть увагу, скільки невідновлюваного.

Рішення: Якщо домінує AnonPages, полюйте на процеси/cgroups. Якщо домінує Cached і MemAvailable низький, можлива трешування файлового кешу через патерни I/O. Якщо домінує Slab/SUnreclaim, підозрюйте зростання памʼяті ядра (часто повʼязане з файловою системою або мережею).

Завдання 4: перевірте, чи вже спрацював OOM killer

cr0x@server:~$ dmesg -T | egrep -i 'oom|out of memory|killed process' | tail -n 20
[Tue Feb  4 10:18:22 2026] Memory cgroup out of memory: Killed process 24198 (java) total-vm:8123456kB, anon-rss:6123456kB, file-rss:12000kB, shmem-rss:0kB, UID:1001 pgtables:14200kB oom_score_adj:0
[Tue Feb  4 10:18:22 2026] oom_reaper: reaped process 24198 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

Що це означає: Це не був «host OOM». Це був memory cgroup OOM, що вбив java всередині обмеженої групи (systemd unit або контейнер). Це найпоширеніша причина, через яку команди кажуть «але на хості була вільна памʼять».

Рішення: Якщо видно cgroup OOM — припиніть дивитись на загальні списки top. Перейдіть до обліку cgroup і перевірте ліміти контейнера/юниту.

Завдання 5: швидко знайдіть процеси з найбільшим RSS

cr0x@server:~$ ps -eo pid,user,comm,rss,pmem --sort=-rss | head -n 15
  PID USER     COMMAND        RSS %MEM
24198 app      java        6321452 19.2
19872 app      node        1823340  5.5
 1321 root     dockerd      612448  1.8
 2012 mysql     mysqld       588120  1.7
 1711 root     prometheus   312880  0.9
  922 root     systemd-jou  188244  0.5
 2666 root     nginx         92240  0.2

Що це означає: RSS — resident set size: фізична памʼять, повʼязана з процесом. Це грубий інструмент, але він швидко виявляє очевидних порушників.

Рішення: Якщо один процес значно перевищує інших і корелює з часом інциденту — у вас головний підозрюваний. Далі: перевірте PSS (бо спільна памʼять може завищувати RSS) і перевірте cgroups/ліміти.

Завдання 6: не обманюйтесь спільною памʼяттю — використовуйте PSS через smaps_rollup

cr0x@server:~$ sudo sh -c 'cat /proc/24198/smaps_rollup | egrep "Pss:|Rss:|Private_Dirty:|Private_Clean:|Shared_Dirty:|Shared_Clean:"'
Rss:                6321452 kB
Pss:                6189021 kB
Shared_Clean:         12400 kB
Shared_Dirty:          1024 kB
Private_Clean:        88000 kB
Private_Dirty:      6219028 kB

Що це означає: PSS (proportional set size) ділить спільні сторінки між процесами. Тут PSS близький до RSS, отже процес дійсно володіє цією памʼяттю (private dirty величезний).

Рішення: Великий Private_Dirty вказує на зростання купи або патерн витоку памʼяті. Якщо PSS значно менший за RSS — ви можете звинувачувати не той процес через спільні бібліотеки або спільні відображення памʼяті.

Завдання 7: якщо є smem, використайте його; це економить час

cr0x@server:~$ smem -r -k -t | head -n 12
  PID User     Command                         Swap      USS      PSS      RSS
24198 app      java                           1024K   6000M   6044M   6173M
19872 app      node                             0K   1600M   1652M   1802M
 1321 root     dockerd                          0K    420M    435M    598M
 2012 mysql    mysqld                           0K    510M    522M    575M
-------------------------------------------------------------------------------
                                        1024K   8530M   8653M   9148M

Що це означає: USS — unique set size (приватна памʼять). PSS — найкраща метрика для системного розподілу витрат памʼяті.

Рішення: Пріоритизуйте процеси з високим PSS/USS при побудові плану помʼякшення. RSS підходить для швидкого огляду; PSS — те, що ви цитуватимете в постмортемі.

Завдання 8: перевірте ліміти контейнера або systemd‑юнита (cgroups v2)

cr0x@server:~$ mount | grep cgroup2
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)

cr0x@server:~$ systemctl status myapp.service --no-pager | egrep 'Memory|Tasks'
     Memory: 6.3G (limit: 6.5G)
     Tasks: 92 (limit: 2048)

Що це означає: Цей юніт має обмеження. Ви можете мати 128GB вільних на хості й усе одно бути вбитими при 6.5GB. Ліміт — частина системного контракту.

Рішення: Якщо кап нижчий за піковий робочий набір — або підвищіть ліміт, або зменшіть споживання памʼяті, або прийміть вбивство як «авто‑скейлер» (не рекомендовано для stateful сервісів).

Завдання 9: перегляньте current/peak та події cgroup

cr0x@server:~$ CG=/sys/fs/cgroup/system.slice/myapp.service
cr0x@server:~$ sudo sh -c "cat $CG/memory.current; cat $CG/memory.max; cat $CG/memory.peak; cat $CG/memory.events"
6848124928
6983510016
6950022144
low 0
high 0
max 12
oom 12
oom_kill 12

Що це означає: memory.max — жорсткий кап. memory.peak показує найгірше спостережуване використання. oom_kill підтверджує, що були вбивання cgroup.

Рішення: Якщо memory.current близько до memory.max, припиніть трактувати це як проблему хоста. Це проблема ліміту або витоку всередині сервісу.

Завдання 10: знайдіть розподіл памʼяті по cgroup (anon/file/slab) на cgroups v2

cr0x@server:~$ sudo sh -c "cat $CG/memory.stat | egrep 'anon |file |slab |sock |shmem |file_mapped|file_dirty|inactive_anon|inactive_file|active_anon|active_file'"
anon 6423011328
file 211345408
shmem 0
slab 142110720
sock 9123840
file_mapped 54476800
file_dirty 122880
inactive_anon 6112147456
active_anon 310863872
inactive_file 188743680
active_file 22601728

Що це означає: Cgroup домінує anon. Це памʼять додатка, не кеш. Slab присутній, але не основна історія.

Рішення: Якщо домінує file, можливо ви кешуєте дані в межах cgroup; можна підлаштувати патерни читання або дозволити більше запасу. Якщо домінує anon, потрібна дисципліна купи, менше обʼєктів в памʼяті або вищий ліміт.

Завдання 11: розслідуйте зростання slab за допомогою slabtop

cr0x@server:~$ sudo slabtop -o | head -n 15
 Active / Total Objects (% used)    : 4821102 / 5012240 (96.2%)
 Active / Total Slabs (% used)      : 118220 / 118220 (100.0%)
 Active / Total Caches (% used)     : 94 / 132 (71.2%)
 Active / Total Size (% used)       : 1289012.40K / 1390024.00K (92.7%)
 Minimum / Average / Maximum Object : 0.01K / 0.28K / 8.00K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
812320 801200  98%    0.19K  38777       21    155108K dentry
610112 605900  99%    0.62K  23832       16    238320K inode_cache
420000 418000  99%    0.10K  10769       39     43076K kmalloc-96

Що це означає: Великі кеші dentry/inode вказують на тиск файлової метадані: багато файлів, багато пошуків по шляхах або навантаження, що швидко створює і видаляє записи (build‑машини, розпаковування архівів, витягання образів контейнерів).

Рішення: Якщо slab — винуватець, не «оптимізуйте додаток». Подивіться на поведінку файлової системи, нескінченні сканування каталогів, рекурсивні бекапи або мільйони дрібних файлів. Також перевірте на помилки ядра або драйверів, якщо slab поводяться дивно/неочікувано.

Завдання 12: перевірте відкриті файли і зростання FD, коли памʼять «зникає»

cr0x@server:~$ sudo lsof -p 24198 | wc -l
18452

cr0x@server:~$ cat /proc/24198/limits | egrep 'Max open files'
Max open files            1048576              1048576              files

Що це означає: Висока кількість FD часто корелює зі споживанням памʼяті (буфери, памʼять сокетів, структури на FD, кеш у юзерспейсі). Не завжди, але це сильний сигнал «щось росте».

Рішення: Якщо FD зростають разом з памʼяттю — ймовірний витік ресурсів (зʼєднання, файли, watcher’и). Виправте витік; підвищення лімітів лише відкладає аварію.

Завдання 13: чи tmpfs або спільна памʼять їдять ОЗП?

cr0x@server:~$ df -hT | egrep 'tmpfs|shm'
tmpfs      tmpfs  3.2G  2.7G  0.5G  85% /run
tmpfs      tmpfs   16G  9.0G  7.0G  57% /dev/shm

Що це означає: tmpfs використовує ОЗП (і swap). Якщо /dev/shm росте, ця памʼять фактично анонімна і може тиснути систему.

Рішення: Якщо ріст tmpfs несподіваний — знайдіть процес, що пише (часто браузери, ML‑навання, системи з інтенсивним IPC) і обмежте або перемістіть сховище.

Завдання 14: перевірте KSM і THP, якщо ви вишукуєте дивні ефекти

cr0x@server:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

cr0x@server:~$ cat /sys/kernel/mm/ksm/run
0

Що це означає: THP може покращити пропускну здатність або створити сплески латентності та фрагментацію памʼяті під деякими навантаженнями. KSM зазвичай вимкнений, якщо ви його явно не вмикали (історично поширене на хостах віртуалізації).

Рішення: Не чіпайте ці налаштування посеред інциденту, якщо ви не знаєте поведінки навантаження. Якщо підозрюєте проблеми THP — збирайте докази (швидкості фолтів, латентність, фрагментація) і міняйте в вікні технічного обслуговування.

Інтерпретація результатів: рішення, які ви зможете відстояти

Коли «used» високе, але «available» у нормі

Якщо MemAvailable у нормі і активність swap близька до нуля, ймовірно у вас звична Linux‑система з великим кешем. Правильна дія зазвичай: нічого не робити, але переконатись, що критичні сервіси не досягли лімітів cgroup.

Команди іноді «вирішують» це, скидаючи кеш. Це не виправлення; це навмисне уповільнення наступного читання.

Коли домінує AnonPages

Саме тут живуть витоки. Але також тут живуть легітимні робочі набори в памʼяті (датастори, JVM‑купи, кеші, які ви навмисно тримаєте в памʼяті).

  • Якщо Private_Dirty процесу зростає стабільно протягом годин/днів — підозрюйте витік або незрегульований кеш.
  • Якщо воно зростає з трафіком і падає при зменшенні трафіку (і GC працює), це може бути нормальна еластичність.
  • Якщо зростання почалося після деплою і не повертається — вважайте це регресією, поки не доведено протилежне.

Коли домінує slab

Памʼять ядра часто — «невидимий податок». Inode, dentry, мережеві буфери, таблиці conntrack і файлові метадані можуть накопичуватися. Slab не є поганим; поганий — безконтрольний slab.

Типові причини:

  • Мільйони дрібних файлів і постійні обходи директорій.
  • Вибух шарів контейнерів і розпакування образів.
  • Системи з великою мережею і великими таблицями conntrack.
  • Баги ядра/драйверів (рідше, але помітно, коли нічого в user‑space не пояснює втрату).

Коли на хості є RAM, але сервіс отримує OOM‑kill

Це проблема cgroups. Межа сервісу обмежена. Або ваш ліміт невірний, або робочий набір виріс, або сервіс почав робити щось нове (наприклад кешувати більше даних).

Операційно — ставтесь до цього як до контрактної проблеми: узгодьте ліміти з реальністю і додайте алертинг для memory.current при наближенні до memory.max.

Жарт №2: OOM killer — єдиний колега, що завжди діє рішуче — на жаль, він ніколи не приходить на постмортем.

Три міні‑історії з корпоративного життя

Міні‑історія 1: інцидент через хибне припущення

Середня компанія тримала пул API‑серверів за балансувальником. На графіках хости виглядали на 40–50% «used» памʼяті, тож усі почувалися в безпеці. Потім після трафікового сплеску API почав випадково повертати 502. Не повний збій — гірше. Частково, що викликало суперечки.

Он‑кол подивився хост‑метрики: багато RAM, CPU ок, диск ок. Перезапустили кілька подів (Kubernetes), стало краще, потім знову погіршилося. Інцидент назвали «мережевою нестабільністю», бо це те, що називають проблемами, які не видно.

Нарешті хтось зазирнув у dmesg на вузлі і побачив повторювані повідомлення Memory cgroup out of memory. Кожне вбивство відбувалося всередині cgroup пода. На хості була памʼять, але поди були обмежені і вмирали під навантаженням. Їхнє припущення — «вільна памʼять на хості = памʼять сервісу» — виявилось помилковим.

Виправлення не було героїчним: поставили ліміти пода на основі спостережуваного PSS під піком, додали запас, і налаштували алерти на memory.events. Після цього той самий трафік давав більшу латентність (прийнятно) замість випадкових вбивств (неприйнятно). Головний урок: «вільна RAM» на ноді не має значення, коли ви живете в cgroups.

Міні‑історія 2: оптимізація, що вдарила назад

Команда платформи даних вирішила зменшити читання з диска на батч‑хості. Хтось підкрутив налаштування, щоб тримати більше даних у памʼяті на рівні додатку: більші кеші, більші буфери, більше паралелізму. Бенчмарки прискорились. Всі були задоволені. Розгорнули це в продакшн.

Через два тижні флот почав свопитися при нормальному навантаженні. Латентність і тривалість джобів впали в невідповідний бік. Команда звинуватила масив зберігання. Потім гіпервізор. Потім «Linux дивно поводиться». Класичний бізнес‑тур.

Коли вони нарешті виміряли AnonPages і PSS по процесах, виявилось, що новий кеш фактично був безконтрольним при певних міксах джобів. Це не був «витік» у строгому сенсі; це був кеш без дисципліни витіснення. Додаток жадібно захоплював памʼять, витісняючи файловий кеш і змушуючи ядро свопити інші сторінки.

Негативний ефект був тонким: оптимізація зменшила читання на малих тестах, але в реальній конкуренції збільшила загальний тиск памʼяті і створила I/O‑помноження через свопінг. Виправлення — поставити жорсткі межі кешів додатку і навантажувати тестами, що імітують продукцію. Перемоги в продуктивності реальні; гординя в памʼяті дорога.

Міні‑історія 3: нудна, але правильна практика, що врятувала день

Сервіс оплати працював під systemd‑юнитами (без контейнерів). У них була непримітна, але корисна звичка: після кожного релізу вони записували базовий профіль памʼяті. Не складний APM — просто скрипт, що зберігав /proc/meminfo, топ‑список RSS з ps і зведення smaps_rollup з привʼязкою до метаданих збірки.

Одного дня вузол почав показувати повільне зростання використання памʼяті. Спочатку жодних алертів, просто повільний дрейф. Он‑кол порівняв поточний PSS‑профіль з тижневим базисом. Дельта була очевидна: приватна dirty памʼять головного процесу виросла на значну величину і трималась на зростаючому тренді.

Вони відкотилися до попередньої версії до того, як OOM killer втрутився. Ніякого інциденту для клієнтів. Пізніше, в спокійний час, вони відтворили витік у staging: нова фічер‑гілка алоціювала обʼєкти, які потім лишалися в глобальній мапі довше, ніж потрібно. Баг виправили швидко, бо команда мала базову лінію і могла сказати «це нове», без суперечок.

Практика не була гламурною. Вона просто переводила розмови про памʼять з релігії в арифметику. У продакшені нудна правильність перемагає хитромудрі здогадки щодня.

Типові помилки (симптом → корінь → виправлення)

1) Симптом: сповіщення «RAM 95% used» постійно спрацьовує, але продуктивність нормальна

Корінь: Алерт базується на used, а не на available. Linux використовує RAM для кешу за задумом.

Виправлення: Алертуйте за MemAvailable (або похідним «available percent») і активністю swap. Оновіть runbook он‑кола, щоб ігнорувати лише «used».

2) Симптом: сервіс отримує OOM‑kill, але хост має багато вільної памʼяті

Корінь: Вдарили по ліміту cgroup (systemd/Kubernetes). Метрики на рівні хоста вводять в оману.

Виправлення: Перевірте memory.max, memory.current і memory.events. Встановіть ліміти на основі спостережуваного PSS/peak. Додайте запас для сплесків і фрагментації.

3) Симптом: swap використовується, але в top немає якихось величезних процесів

Корінь: Спільна памʼять спотворює уявлення RSS; або памʼять споживає slab ядра; або кілька середніх процесів в сумі створюють тиск.

Виправлення: Використовуйте PSS (smem або smaps_rollup), потім перевірте slab (slabtop) і /proc/meminfo.

4) Симптом: памʼять постійно зростає після деплою і врешті OOM

Корінь: Витік або неконтрольований кеш у user‑space; іноді зміна у трафіку відкриває шлях росту.

Виправлення: Підтвердіть ріст за Private_Dirty в часі. Накладіть тимчасовий жорсткий кап (налаштування) як помʼякшення, потім отскануйте купу відповідними інструментами для JVM, Go, Python. Не «просто додайте swap».

5) Симптом: slab росте, домінують dentry/inode_cache

Корінь: Час життя метаданих файлової системи: великі каталоги, рекурсивні сканування, build‑артефакти, буря логів, розпакування образів контейнерів.

Виправлення: Зменште кількість і churn файлів; виправте скрипти, що роблять повторні рекурсивні find; налаштуйте ротацію логів; уникайте вибуху дрібних файлів на спільних хостах. Якщо це build‑хост — ізолюйте робочі навантаження.

6) Симптом: випадкові стрибки латентності, kswapd вимагає CPU, але графіки RAM виглядають «окей»

Корінь: Витрати на reclaim і compacting; потенційні побічні ефекти THP; фрагментація памʼяті.

Виправлення: Корелюйте з статистикою paging і reclaim. Розгляньте режим THP madvise для певних навантажень, але лише після тестування. Частіше виправлення — «менше тиску памʼяті», а не «інші графіки».

7) Симптом: «кеш величезний, drop_caches допомагає»

Корінь: Скидання кешу маскує основну проблему — витік процесу, неконтрольну метадані або недооцінений cgroup; ядро все одно заповнить кеш згодом.

Виправлення: Не робіть drop_caches рутинною операцією. Виміряйте, що викликає ріст кешу; виправте навантаження або встановіть реалістичні бюджети памʼяті.

Чеклісти / покроковий план

10‑хвилинний чекліст on‑call (робіть у цьому порядку)

  1. Запустіть free -h. Якщо available низький — продовжуйте; якщо ні — все одно перевірте swap і cgroups.
  2. Запустіть vmstat 1 5. Якщо si/so постійно ненульові — працюйте як з реальним тиском памʼяті.
  3. Прогляньте meminfo grep. Визначте: anon‑heavy vs cache‑heavy vs slab‑heavy.
  4. Перевірте dmesg на предмет OOM. Якщо це cgroup OOM — зупиніться і перемкніться на cgroups.
  5. Складіть список топ RSS процесів через ps; знайдіть кандидатів.
  6. Підтвердіть кандидатів через smaps_rollup (PSS/private dirty).
  7. Якщо є контейнери/systemd: прочитайте memory.current, memory.max, memory.events і memory.stat.
  8. Якщо процеси не сходяться — розслідуйте slab через slabtop.
  9. Перевірте використання tmpfs (df -hT для tmpfs/shm).
  10. Виберіть дію: перезапуск винуватця, підвищення ліміту, виправлення витоку, зменшення метаданого churn, або масштабування. Уникайте перезавантаження, якщо це не необхідно для зупинки кровотечі.

Чекліст рішень: що змінювати залежно від знахідок

  • Один процес володіє памʼяттю (високий PSS/private dirty): помʼякшити перезапуском/відкатом; впровадити обмеження; відлагоджувати витік.
  • Багато процесів у сумі створюють тиск: зменшити паралелізм, масштабувати горизонтально або перемістити важкі задачі з спільних хостів.
  • Кап cgroup занадто низький: підвищити кап з запасом; правильно визначити requests/limits; алертити при наближенні до капа.
  • Slab — головний споживач: виправити файловий/мережевий churn; зменшити кількість файлів; дослідити підсистеми ядра.
  • Активність swap — проблема: зменшити використання памʼяті; розглянути зміну swappiness лише після переконливих доказів, що це допоможе.

Післяінцидентний чекліст (щоб не повторюватися)

  1. Заберіть знімок профілю памʼяті: meminfo, топ PSS, cgroup‑статистика, зведення slabtop.
  2. Додайте алерти, які відображають реальність: MemAvailable, I/O swap, cgroup OOM‑kill та зростання slab.
  3. Встановіть бюджети: бюджети памʼяті на сервіс і протестуйте їх при реалістичній конкуренції.
  4. Документуйте «відомо добрий» базовий профіль для кожного релізу, щоб регресії були очевидні.

Факти й історичний контекст (те, що пояснює сучасну дивність)

  1. Поле MemAvailable — відносно молоде в термінах ядра; його додали, бо «вільна памʼять» погано прогнозує відновлювану памʼять.
  2. Linux навмисно використовує вільну RAM для файлового кешу, щоб уникнути повільних операцій з диском; низький «free» часто означає, що ядро робить свою роботу.
  3. Рішення OOM‑killer є евристичним: він призначає «оцінки шкідливості» процесам. Це не моральний вирок — це сортування під час кризи.
  4. cgroups зробили нормою «один хост — багато світів памʼяті». Контейнер може OOM‑итись, поки вузол у порядку, бо вузол не є всесвітом контейнера.
  5. RSS може переносити надмірний рахунок спільних сторінок. Тому існує PSS: для справедливого розподілу спільних сторінок між процесами.
  6. tmpfs — це RAM‑підтримуване сховище (та також підтягує swap). Зберігання «тимчасових файлів» у tmpfs може перетворитися на «постійний тиск памʼяті».
  7. Slab кеші — це функція продуктивності: ядра кешують обʼєкти типу dentry/inode, бо постійне їх виділення/звільнення дорого обходиться.
  8. Transparent Huge Pages набули популярності для приросту пропускної здатності, але також внесли операційні компроміси: витрати на compacting, фрагментацію і чутливість до латентності.

FAQ

1) Чому free показує майже нічого «free» на здоровій системі?

Тому що Linux використовує RAM як кеш. Дивіться на available, а не на free. «Free» — це здебільшого невикористані сторінки; «available» — оцінка відновлюваних сторінок плюс невикористані.

2) Чи варто скидати кеш, щоб «звільнити памʼять»?

Майже ніколи в продакшені. Скидання кешу — це як висипати інструменти на підлогу, щоб верстак виглядав чистим. Це може тимчасово полегшити тиск, але зазвичай шкодить продуктивності і приховує корінну причину.

3) У чому різниця між RSS, VIRT, USS і PSS?

VIRT — віртуальний адресний простір; може бути величезним і неінформативним. RSS — resident pages у RAM, але він переобліковує спільні сторінки. USS — приватна памʼять (унікальна). PSS — найкраща метрика для системного розподілу, бо пропорційно ділить спільні сторінки.

4) OOM‑killer вбив мій найбільший процес. Чи означає це, що саме він був причиною?

Не обовʼязково. Це був найзручніший для вбивства процес за евристикою в той момент. Причина може бути агрегованим тиском, лімітом cgroup або зростанням невідновлюваної памʼяті ядра.

5) Як зрозуміти, чи це cgroup OOM?

Подивіться в dmesg на «Memory cgroup out of memory» і перевірте /sys/fs/cgroup/.../memory.events. Якщо там інкрементує oom_kill — це cgroup‑проблема.

6) Чому swap показує ненульову величину навіть при наявності вільної RAM?

Ядро може перемістити «холодні» анонімні сторінки в swap, щоб тримати більше файлового кешу «гарячим». Ненульовий swap сам по собі не завжди поганий. Важливі активні швидкості swap‑in/out і їхній вплив на латентність.

7) Що зазвичай викликає вибухове зростання slab?

Час життя файлових метаданих (dentry/inode), мережеві таблиці, і іноді баги ядра. slabtop покаже, який кеш росте; це вкаже на підсистему.

8) Мій контейнер показує низьке використання памʼяті всередині, але хост каже, що воно величезне. Хто правий?

Вони можуть бути обидва «праві», бо повідомляють про різні області і метрики. Для дебагу контейнера довіряйте файлам cgroup: memory.current і memory.stat. Для дебагу хоста використовуйте meminfo плюс PSS по процесах.

9) Як швидко виявити витік памʼяті без профайлерів?

Шукайте монотонне зростання Private_Dirty (через /proc/PID/smaps_rollup) і збільшення PSS з часом, корельоване з аптаймом або трафіком. Як помʼякшення — перезапуск/відкат і постановка капу, якщо можливо.

10) Чи коли-небудь правильно додати більше RAM?

Так — коли робочий набір дійсно більший за машину і вартість оптимізації перевищує вартість RAM. Але підтвердіть це PSS і сигналами тиску спочатку; не платіть за апарат, щоб покривати витік.

Висновок: наступні кроки, які реально зменшують сторінки

Коли ви намагаєтесь відповісти «що їсть ОЗП», не дивіться лише на одне число. Використайте послідовність, яка змушує говорити правду: сигнали тиску, розподіл по відсіках, потім атрибуція правильною метрикою (PSS), потім область (cgroups vs хост), і нарешті — памʼять ядра, якщо user‑space не пояснює втрату.

Практичні наступні кроки:

  • Оновіть алерти, щоб пріоритетними були MemAvailable, активність swap і cgroup OOM‑kills — а не сирий «used».
  • Додайте легкий скрипт знімка памʼяті до інструментів інциденту (meminfo + топ PSS + cgroup‑статистика + зведення slab).
  • Правильно визначайте ліміти памʼяті з використанням memory.peak і PSS‑базових профілів, додаючи запас.
  • Для повторних порушників: реалізуйте ліміти для внутрішніх кешів додатку і розглядайте будь‑яке монотонне зростання private dirty як регресію, поки не доведено протилежне.

Якщо нічого не робите більше: наступного разу, коли прийде пейдж, перш ніж формувати думку — прочитайте /proc/meminfo. З ядром сперечатися важче, коли воно дає вам розбивку по рядках.

← Попередня
Катастрофа при зміні розміру розділу: скасуйте й завантажтеся знову
Наступна →
DNS: ваш домен працює… поки раптом ні — пояснення пастки делегування

Залишити коментар