Linux : Méthode en 10 minutes pour trouver ce qui consomme réellement votre RAM

Cet article vous a aidé ?

Vous recevez une alerte : « Host is out of memory. » Vous vous connectez en SSH, lancez free -h, et il vous dit que la mémoire est « used ». Très utile. Comme un détecteur de fumée qui se contenterait de dire « FUMÉE ».

Le secret est d’arrêter de considérer la RAM comme un seul pot. Linux la divise en seaux qui comptent opérationnellement : mémoire anonyme (vos processus), cache basé sur fichiers (I/O rapide), slabs du noyau (métadonnées) et limites imposées par les cgroups. En dix minutes vous pouvez généralement identifier le vrai coupable — et choisir la bonne correction au lieu du classique : redémarrer et prier.

Méthode de diagnostic rapide

Ceci est l’ordre qui vous évite de courir après des fantômes. Vous chassez d’abord le plus gros seau, puis vous resserrez vers le coupable, puis vous validez avec la propre comptabilité du noyau.

Première étape : S’agit-il d’une vraie pression mémoire ou juste du cache de pages ?

  • Vérifiez free -h et concentrez-vous sur available, pas sur used.
  • Vérifiez l’activité de swap (vmstat / sar) et les défauts de page majeurs si vous les avez.

Deuxième étape : Le noyau est-il sous pression mémoire (risque OOM) et pourquoi ?

  • Vérifiez dmesg pour les journaux de l’OOM killer et quel cgroup l’a déclenché.
  • Consultez /proc/meminfo pour la répartition anon vs file vs slab.

Troisième étape : Identifiez les plus gros consommateurs avec la bonne métrique

  • Commencez par le RSS par processus pour repérer les coupables évidents (ps).
  • Puis utilisez le PSS quand la mémoire partagée rend le RSS trompeur (smem ou /proc/*/smaps_rollup).
  • Si des conteneurs sont impliqués, vérifiez d’abord les cgroups ; « top sur l’hôte » n’est pas un outil de comptabilité pour conteneurs.

Quatrième étape : Si les processus n’expliquent pas tout, suspectez la mémoire du noyau

  • Croissance des slabs : slabtop, /proc/slabinfo, la section Slab dans meminfo.
  • Les piles du noyau, les tables de pages et la mémoire non récupérable peuvent faire tomber un hôte silencieusement.

Cinquième étape : Validez la voie de correction avant d’agir drastiquement

  • Tuez/redémarrez seulement le coupable, pas la machine entière.
  • Fixez des limites sensées (systemd/Kubernetes) après avoir compris l’usage en steady-state.
  • Corrigez les fuites avec des preuves : courbes de croissance, pas des intuitions.

Modèle mental : ce que signifie vraiment « used RAM »

Linux utilise la RAM de manière agressive parce que la mémoire inoccupée est une opportunité gaspillée. Il remplira la mémoire avec le cache de fichiers, les dentries, le cache d’inodes et d’autres aides de performance. Ce n’est pas une fuite ; c’est le noyau qui fait son travail.

La question opérationnelle n’est pas « Pourquoi used est élevé ? » mais « Le système manque-t-il de mémoire récupérable ? » C’est pourquoi free affiche une estimation available. « Available » signifie approximativement : si vous lancez une nouvelle charge maintenant, combien le noyau peut-il libérer sans provoquer une catastrophe ?

La pression mémoire devient réelle quand :

  • Le swap est activement utilisé et surtout si les taux de swap-in/out sont non négligeables.
  • Le reclaim direct bloque vos charges : pics de latence, CPU en mode noyau, kswapd occupé.
  • L’OOM killer apparaît, soit au niveau de l’hôte soit à l’intérieur d’un cgroup (les conteneurs aiment mourir silencieusement).
  • Le slab ou la mémoire non récupérable croît, laissant moins d’espace récupérable.

Voici la répartition à garder en tête :

  • Mémoire anonyme (AnonPages) : heaps, stacks, JIT, mallocs. C’est ce que consomment « les applications ».
  • Mémoire basée sur fichiers (Cached) : cache du système de fichiers. Récupérable quand nécessaire, généralement.
  • Slab (SReclaimable + SUnreclaim) : caches/métadonnées du noyau. Partiellement récupérable.
  • Mémoire engagée : promesses. Toutes les promesses ne deviennent pas dette, mais surveillez l’overcommit.
  • Cgroups : limites. Vous pouvez avoir beaucoup de RAM sur l’hôte et subir un OOM à l’intérieur d’un conteneur.

Une idée paraphrasée d’une légende de la fiabilité : paraphrased idea — John Allspaw a soutenu que les incidents de production sont souvent du « travail normal » en collision avec des hypothèses ; les pages mémoire suivent parfaitement ce modèle.

Blague #1 : Le dépannage mémoire, c’est comme un régime : les chiffres sont réels, mais les étiquettes vous mentent.

La méthode en 10 minutes (tâches et commandes)

Ci‑dessous des tâches pratiques que vous pouvez lancer sur n’importe quelle machine Linux (bare metal, VM, hôte de conteneurs). Chaque tâche inclut : la commande, ce que signifie la sortie, et la décision à prendre.

Tâche 1 : Vérification rapide avec 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

Ce que ça signifie : Le nombre clé est available. Ici il est ~2.9Gi, ce qui n’est pas confortable si la charge monte en flèche. Le swap est déjà utilisé (2.1Gi), ce qui suggère une vraie pression.

Décision : Si available est faible et que le swap est utilisé ou croissant, poursuivez l’investigation. Si available est sain et que le swap est calme, votre « used RAM » est probablement du cache et le problème peut être ailleurs (disque ou CPU).

Tâche 2 : Vérifier le paging actif et le comportement de reclaim avec 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

Ce que ça signifie : si/so sont les swap-in/out par seconde. Des valeurs non nulles sur la durée signifient que le noyau jongle activement avec les pages. Surveillez aussi wa (I/O wait) et b (processus bloqués).

Décision : Si le swap-out persiste, vous êtes sous pression mémoire. Votre prochaine étape est d’identifier quel seau grossit : processus, cache, slab ou cgroups.

Tâche 3 : Lire le sérum de vérité : /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

Ce que ça signifie : AnonPages est énorme : les processus sont le principal consommateur. Cached est relativement petit, donc ce n’est pas « juste le cache de pages ». Le slab est non trivial ; notez combien est non récupérable.

Décision : Si AnonPages domine, cherchez dans les processus/cgroups. Si Cached domine et que MemAvailable est bas, vous pourriez thrashing sur le cache de fichiers dû aux patterns I/O. Si Slab/SUnreclaim domine, suspectez une croissance de la mémoire du noyau (souvent liée au système de fichiers ou au réseau).

Tâche 4 : Vérifier si l’OOM killer a déjà agi

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

Ce que ça signifie : Ce n’était pas un « OOM hôte ». C’était un OOM de cgroup, tuant java à l’intérieur d’un groupe limité (unit systemd ou conteneur). C’est la raison la plus courante pour laquelle les équipes jurent « mais l’hôte avait de la mémoire libre ».

Décision : Si un OOM de cgroup apparaît, arrêtez de regarder les listes top au niveau hôte. Passez à la comptabilité des cgroups et aux limites des conteneurs/unités.

Tâche 5 : Identifier rapidement les processus avec le plus grand 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

Ce que ça signifie : Le RSS est le Resident Set Size : la RAM physique mappée dans le processus. C’est un instrument grossier, mais il attrape vite les coupables évidents.

Décision : Si un processus dépasse largement les autres et que cela corrèle avec le moment de l’incident, vous avez un suspect principal. Vérifiez ensuite avec le PSS (la mémoire partagée peut gonfler le RSS) et regardez les cgroups/limites.

Tâche 6 : Ne vous laissez pas tromper par la mémoire partagée — utilisez le PSS via 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

Ce que ça signifie : Le PSS (proportional set size) répartit les pages partagées entre les processus. Ici le PSS est proche du RSS, donc le processus est réellement propriétaire de cette mémoire (private dirty est massif).

Décision : Un Private_Dirty élevé suggère une croissance du heap ou des fuites mémoire. Si le PSS est bien inférieur au RSS, vous pourriez accuser à tort un processus à cause des bibliothèques partagées ou des mappings partagés.

Tâche 7 : Si vous avez smem, utilisez‑le ; ça fait gagner du temps

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

Ce que ça signifie : L’USS est le Unique Set Size (mémoire privée). Le PSS est la meilleure métrique d’attribution « qui consomme réellement » au niveau système.

Décision : Priorisez les processus en tête par PSS/USS lors de l’élaboration d’un plan d’atténuation. Le RSS suffit pour un aperçu rapide ; le PSS est ce que vous citez dans un postmortem.

Tâche 8 : Vérifier les limites du conteneur ou de l’unité 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)

Ce que ça signifie : Cette unité est plafonnée. Vous pouvez avoir 128GB libres sur l’hôte et malgré tout être tué à 6.5GB. La limite fait partie du contrat du système.

Décision : Si le plafond est en dessous du jeu de travail maximal, soit augmentez la limite, réduisez l’usage mémoire, soit acceptez le kill comme « autoscaler » (non recommandé pour les services état-plein).

Tâche 9 : Examiner memory.current/peak et events du 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

Ce que ça signifie : memory.max est le plafond strict. memory.peak indique l’utilisation maximale observée. oom_kill confirme qu’il y a eu des kills de cgroup.

Décision : Si memory.current est proche de memory.max, cessez de traiter cela comme un problème global d’hôte. C’est un problème de limite ou une fuite à l’intérieur du périmètre du service.

Tâche 10 : Trouver la répartition mémoire par cgroup (anon/file/slab) sur 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

Ce que ça signifie : Le cgroup est dominé par anon. C’est la mémoire applicative, pas le cache. Le slab est présent mais pas l’histoire principale.

Décision : Si file domine, vous pourriez mettre en cache à l’intérieur du cgroup ; vous pouvez ajuster les patterns de lecture ou permettre plus de marge. Si anon domine, il faut discipliner le heap, diminuer les objets en mémoire ou augmenter la limite.

Tâche 11 : Enquêter sur la croissance des slabs avec 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

Ce que ça signifie : De gros caches dentry/inode indiquent une pression sur les métadonnées du système de fichiers : beaucoup de fichiers, beaucoup de recherches de chemins, ou une charge qui génère un fort turnover de dentries (machines de build, dépaquetage, extraction d’images de conteneurs).

Décision : Si le slab est le coupable, n’« optimisez » pas l’application. Regardez le comportement du système de fichiers, les scans de répertoires incontrôlés, les sauvegardes récursives ou des millions de petits fichiers. Vérifiez aussi les bugs du noyau ou des pilotes si les slabs sont étranges/inattendus.

Tâche 12 : Vérifier les fichiers ouverts et la croissance des FD quand la mémoire « disparaît »

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

Ce que ça signifie : Un nombre élevé de descripteurs de fichiers (FD) corrèle souvent avec l’usage mémoire (buffers, mémoire socket, structures par-FD, cache en user-space). Pas toujours, mais c’est un fort signal « quelque chose croît ».

Décision : Si les FD augmentent régulièrement avec la mémoire, vous avez probablement une fuite de ressource (connexions, fichiers, watchers). Corrigez la fuite ; augmenter les limites ne fait que retarder la panne.

Tâche 13 : Voir si tmpfs ou la mémoire partagée consomment la RAM

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

Ce que ça signifie : tmpfs utilise la RAM (et le swap). Si /dev/shm grossit, cette mémoire est effectivement anonyme et peut mettre la pression sur le système.

Décision : Si la croissance de tmpfs est inattendue, trouvez le processus qui écrit (souvent navigateurs, workloads ML, systèmes IPC intensifs) et plafonnez ou déplacez le stockage.

Tâche 14 : Vérifier KSM et THP si vous courez après des bizarreries

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

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

Ce que ça signifie : THP peut améliorer la performance ou créer des pics de latence et de la fragmentation mémoire selon certaines charges. KSM est généralement désactivé sauf si vous l’avez activé (historique sur hôtes de virtualisation).

Décision : Ne basculez pas ces options en plein incident à moins de connaître le comportement de votre charge. Si vous suspectez THP, capturez des preuves (taux de défauts, latence, fragmentation) et changez en fenêtres de maintenance.

Interpréter les résultats : décisions défendables

Quand « used » est élevé mais « available » est correct

Si MemAvailable est sain et que l’activité de swap est proche de zéro, vous avez probablement un système Linux normal et riche en cache. La bonne action est généralement : ne rien faire, mais vérifiez que vous n’atteignez pas les limites de cgroup pour les services critiques.

Les équipes « corrigent » parfois cela en vidant les caches. Ce n’est pas une solution ; c’est rendre la prochaine lecture volontairement plus lente.

Quand AnonPages domine

C’est là que résident les fuites. Mais c’est aussi là où vivent des jeux de travail légitimes en mémoire (stores de données, heaps JVM, caches que vous avez volontairement mis en mémoire).

  • Si le Private_Dirty d’un processus augmente de façon continue sur des heures/jours, suspectez une fuite ou un cache sans limites.
  • Si cela augmente avec le trafic et redescend quand le trafic baisse (et que le GC s’exécute), cela peut être une élasticité normale.
  • Si ça monte après un déploiement et ne redescend jamais, traitez-le comme une régression jusqu’à preuve du contraire.

Quand le slab domine

La mémoire du noyau est souvent « la taxe invisible ». Inodes, dentries, tampons réseau, tables conntrack et métadonnées peuvent s’ajouter. Le slab n’est pas mauvais ; le slab non borné l’est.

Causes typiques :

  • Des millions de petits fichiers et des traversées de répertoires constantes.
  • Extraction d’images de conteneurs et couches de conteneurs en explosion.
  • Systèmes réseau intensifs avec de grandes tables de tracking de connexions.
  • Bugs du noyau/du pilote (plus rares, mais vous le saurez car rien en user space n’explique la perte).

Quand la machine a de la RAM mais le service se fait OOM-killer

C’est un problème de cgroups. La frontière du service est limitée. Soit votre limite est erronée, soit votre jeu de travail a grandi, soit votre service fait quelque chose de nouveau (comme mettre plus de données en cache).

Opérationnellement, traitez cela comme un problème de contrat de production : alignez les limites sur la réalité et ajoutez des alertes sur memory.current approchant memory.max.

Blague #2 : L’OOM killer est le seul coéquipier qui agit toujours de façon décisive — malheureusement, il n’assiste jamais au postmortem.

Trois micro-récits tirés de la vie en entreprise

Micro-récit 1 : L’incident causé par une mauvaise hypothèse

Une entreprise de taille moyenne exécutait une flotte de serveurs API derrière un load balancer. Leurs tableaux de bord montraient les hôtes à 40–50% de mémoire « used », et tout le monde se sentait en sécurité. Puis, après un pic de trafic, l’API a commencé à renvoyer des 502 aléatoires. Pas une panne complète — pire. Une panne partielle qui fait se disputer tout le monde.

Le on-call regardait les métriques de l’hôte : beaucoup de RAM, CPU OK, disque OK. Ils ont redémarré quelques pods (Kubernetes), ça s’est amélioré, puis dégradé à nouveau. L’incident a été étiqueté « instabilité réseau » parce que c’est ce que l’on appelle quand on ne voit pas le vrai problème.

Finalement quelqu’un a tailé dmesg sur un nœud et a vu des messages répétés Memory cgroup out of memory. Chaque kill avait lieu à l’intérieur d’un pod. L’hôte avait de la mémoire, mais les pods étaient plafonnés et se faisaient tuer sous des pics. Leur hypothèse — la mémoire libre de l’hôte équivaut à la mémoire du service — était le mensonge.

La correction n’a pas été héroïque : définir la limite de pod en se basant sur le PSS observé aux pics, ajouter une marge de sécurité et alerter sur les incréments de memory.events. Après cela, le même pattern de trafic a généré une latence plus élevée (acceptable) au lieu de morts aléatoires (inacceptables). La plus grande leçon : la « RAM libre » du nœud est hors de propos quand vous vivez dans des cgroups.

Micro-récit 2 : L’optimisation qui a mal tourné

Une équipe plateforme data voulait réduire les lectures disque sur un hôte de traitements batch. Quelqu’un a modifié un réglage pour garder plus de données en mémoire au niveau applicatif : caches in-process plus grands, buffers plus volumineux, plus de parallélisme. Les benchmarks étaient plus rapides. Tout le monde célébrait. Ils ont déployé.

Deux semaines plus tard, la flotte a commencé à swapper sous charge normale. Latence et durée des jobs ont dérapé. L’équipe a d’abord blâmé le stockage. Puis l’hyperviseur. Puis « Linux buggé ». Classique.

Quand ils ont enfin mesuré AnonPages et le PSS par processus, ils ont découvert que le nouveau cache était essentiellement non borné avec certains mixes de jobs. Ce n’était pas une « fuite » stricte ; c’était un cache sans discipline d’éviction. L’application thésaurisait la mémoire, repoussant le cache du système de fichiers et forçant le noyau à swapper d’autres pages.

Le retour de bâton était subtil : leur « optimisation » réduisait les lectures disque à petite échelle, mais à la concurrence production elle augmentait la pression mémoire totale et créait une amplification I/O via le swapping. La correction a été de poser des plafonds stricts sur les caches applicatifs, et de faire des tests de charge avec une concurrence et un churn de données réalistes. Les gains mémoire existent ; l’arrogance mémoire coûte cher.

Micro-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Un service de paiement tournait sur des unités systemd (pas de conteneurs). Ils avaient une habitude peu glamour : chaque service avait un profil mémoire de base enregistré après chaque release. Pas un APM sophistiqué. Juste un script capturant /proc/meminfo, les tops RSS ps, et des résumés smaps_rollup par processus, stockés avec les métadonnées de build.

Un après-midi, un nœud a commencé à montrer une utilisation mémoire croissante. Pas encore d’alertes, juste une lente dérive. Le on-call a comparé le profil PSS actuel à celui de la semaine précédente. Le delta était évident : la mémoire privée dirty du processus principal était plus élevée et en tendance ascendante.

Ils ont rollbacké avant que l’OOM killer n’intervienne. Pas d’incident client. Plus tard, au calme, ils ont reproduit la fuite en staging : un nouveau chemin fonctionnel allouait des objets stockés dans une map globale plus longtemps que prévu. Le bug a été corrigé rapidement parce que l’équipe avait une base de référence et pouvait dire « ceci est nouveau » sans débat.

La pratique n’était pas glamour. Elle a simplement remplacé les discussions mémorielles religieuses par de l’arithmétique. En production, la correction ennuyeuse bat le flair ingénieux, tous les jours.

Erreurs fréquentes (symptômes → cause racine → correction)

1) Symptom : « RAM is 95% used » alerte se déclenche sans arrêt, mais les performances sont correctes

Cause racine : L’alerte est basée sur used plutôt que sur available. Linux utilise la RAM pour le cache par conception.

Correction : Alerter sur MemAvailable (ou un « pourcentage disponible » dérivé), plus l’activité de swap. Modifiez le runbook on-call pour ignorer le seul « used ».

2) Symptom : Le service se fait OOM-killer alors que l’hôte a beaucoup de mémoire

Cause racine : Limite de mémoire du cgroup atteinte (systemd/Kubernetes). Les métriques au niveau hôte sont trompeuses.

Correction : Inspecter memory.max, memory.current, et memory.events. Définir les limites sur la base du PSS/peak observé. Ajouter une marge pour les pics et la fragmentation.

3) Symptom : Le swap est utilisé, mais aucun processus ne semble énorme dans top

Cause racine : La mémoire partagée gonfle/dégonfle les perceptions RSS ; ou le slab du noyau consomme ; ou plusieurs processus moyens s’additionnent pour faire pression.

Correction : Utiliser le PSS (smem ou smaps_rollup), puis vérifier le slab (slabtop) et /proc/meminfo.

4) Symptom : La mémoire continue d’augmenter après un déploiement, puis fini par OOM

Cause racine : Fuite ou cache sans bornes en user space ; parfois un changement du mix de trafic déclenche un chemin de croissance.

Correction : Confirmer la croissance avec la tendance Private_Dirty. Appliquer un plafonnement (config) comme mitigation, puis déboguer avec les outils de heap adaptés au runtime (JVM, Go, Python). Ne « ajoutez pas juste du swap ».

5) Symptom : Le slab croît, dentry/inode_cache dominent

Cause racine : Turnover des métadonnées du système de fichiers : grands répertoires, scans récursifs, artefacts de build, tempêtes de logs, extraction d’images de conteneurs.

Correction : Réduire le nombre de fichiers et le churn ; corriger les scripts qui font des find récursifs répétés ; faire une rotation de logs appropriée ; éviter d’exploser les petits fichiers sur des hôtes partagés. Si c’est un hôte de build, isoler les charges.

6) Symptom : Pics de latence aléatoires, CPU kswapd, mais les graphes RAM semblent « ok »

Cause racine : Reclaim mémoire et coût de compaction ; effets secondaires possibles des THP ; fragmentation mémoire.

Correction : Corréler avec les stats de paging et de reclaim. Envisager le mode THP madvise pour certaines charges, mais seulement après test. La solution est souvent « moins de pression mémoire », pas « d’autres graphiques ».

7) Symptom : « Le cache est énorme, drop_caches le résout »

Cause racine : Vider le cache masque un problème sous-jacent comme un processus en fuite, des métadonnées incontrôlées ou un cgroup sous-dimensionné ; le noyau refera le cache de toute façon.

Correction : Ne pas industrialiser drop_caches comme action de routine. Mesurez ce qui provoque la croissance du cache ; corrigez la charge ou définissez des budgets mémoire réalistes.

Listes de contrôle / plan étape par étape

Checklist on-call en 10 minutes (faire dans cet ordre)

  1. Exécuter free -h. Si available est bas, poursuivre ; sinon, valider le swap et les cgroups quand même.
  2. Exécuter vmstat 1 5. Si si/so sont persistants non nuls, considérer comme vraie pression mémoire.
  3. Faire le grep meminfo. Décider : anonim-heavy vs cache-heavy vs slab-heavy.
  4. Vérifier dmesg pour OOM. Si c’est un OOM cgroup, s’arrêter et pivoter vers les cgroups.
  5. Lister les top RSS avec ps ; identifier des candidats.
  6. Valider les candidats avec smaps_rollup (PSS/private dirty).
  7. Si conteneurs/unités systemd impliqués : lire memory.current, memory.max, memory.events et memory.stat.
  8. Si les processus n’expliquent pas tout : investiguer le slab avec slabtop.
  9. Vérifier l’usage tmpfs (df -hT pour tmpfs/shm).
  10. Choisir l’action : redémarrer le coupable, augmenter la limite, corriger la fuite, réduire le churn de fichiers ou scaler horizontalement. Éviter le reboot sauf si nécessaire pour arrêter l’hémorragie.

Checklist décisionnelle : ce que vous changez selon vos trouvailles

  • Un seul processus possède la mémoire (PSS/private dirty élevés) : Atténuer par restart/rollback ; implémenter des caps ; déboguer la fuite.
  • Beaucoup de processus s’additionnent : Réduire la concurrence, scaler horizontalement, ou déplacer les jobs lourds hors des hôtes partagés.
  • Le cap cgroup est trop bas : Augmenter le cap avec marge ; dimensionner correctement requests/limits ; alerter quand on approche du cap.
  • Le slab est le principal consommateur : Corriger le churn filesystem/réseau ; réduire le nombre de fichiers ; investiguer les sous‑systèmes noyau.
  • L’activité de swap est le problème : Réduire l’usage mémoire ; envisager de réduire le swappiness seulement après vérification que cela aide la charge.

Checklist post‑incident (pour ne pas revivre la même chose)

  1. Capturer un snapshot mémoire : meminfo, liste top PSS, stats cgroup, résumé slabtop.
  2. Ajouter des alertes qui reflètent la réalité : MemAvailable, I/O de swap, kills OOM de cgroup et croissance du slab.
  3. Fixer des budgets : budgets mémoire par service et les tester sous une concurrence réaliste.
  4. Documenter la « known good » baseline par release pour que les régressions soient évidentes.

Faits & contexte historique (ce qui explique les bizarreries d’aujourd’hui)

  1. Le champ MemAvailable est relativement moderne en termes de noyau ; il a été ajouté parce que « mémoire libre » était un terrible prédicteur de la mémoire récupérable.
  2. Linux utilise intentionnellement la RAM libre pour le page cache afin d’éviter des lectures disque lentes ; voir peu de « free » est souvent un signe que le noyau fait son travail.
  3. Les décisions de l’OOM killer sont heuristiques : il attribue des scores de « gravité » aux processus. Ce n’est pas un jugement moral ; c’est du triage sous contrainte.
  4. Les cgroups ont rendu « un hôte, plusieurs mondes mémoire » normal. Un conteneur peut OOM alors que le nœud est sain, car le nœud n’est pas l’univers du conteneur.
  5. Le RSS peut surcompter les pages partagées. C’est pourquoi le PSS existe : attribuer les pages partagées plus équitablement entre les processus.
  6. tmpfs est basé sur la RAM (et le swap). Stocker des « fichiers temporaires » en tmpfs peut devenir une véritable pression mémoire « permanente ».
  7. Les caches slab sont une fonctionnalité de performance : les noyaux mettent en cache des objets comme dentries/inodes parce qu’allouer/libérer constamment est coûteux.
  8. Transparent Huge Pages a gagné en popularité pour les gains de débit, mais a aussi introduit de nouveaux compromis opérationnels : coût de compaction, fragmentation et sensibilité à la latence.

FAQ

1) Pourquoi free montre presque aucune mémoire « free » sur un système sain ?

Parce que Linux utilise la RAM comme cache. Regardez available, pas free. « Free » correspond surtout aux pages inutilisées ; « available » estime les pages récupérables plus les pages inutilisées.

2) Dois‑je vider les caches pour « libérer de la mémoire » ?

Presque jamais en production. Vider les caches revient à vider votre boîte à outils sur le sol pour que l’établi ait l’air propre. Cela peut soulager brièvement la pression, mais nuit généralement aux performances et masque les causes.

3) Quelle est la différence entre RSS, VIRT, USS et PSS ?

VIRT est l’espace d’adressage virtuel ; il peut être énorme et signifier peu de choses. RSS est les pages résidentes en RAM, mais il surcompte les pages partagées. USS est la mémoire privée (unique). PSS est la meilleure métrique d’attribution système car il partage proportionnellement les pages partagées.

4) L’OOM killer a tué mon plus gros processus. Est‑ce qu’il était forcément la cause ?

Pas nécessairement. C’était le processus le plus « killable » selon l’heuristique à ce moment. La cause peut être une pression agrégée, une limite de cgroup, ou une croissance de mémoire noyau non récupérable.

5) Comment savoir s’il s’agit d’un OOM de cgroup ?

Regardez dans dmesg pour « Memory cgroup out of memory » et vérifiez /sys/fs/cgroup/.../memory.events. Si oom_kill y incrémente, c’est un problème de cgroup.

6) Pourquoi l’utilisation du swap est non nulle même si j’ai de la RAM libre ?

Le noyau peut déplacer des pages anonymes froides vers le swap pour garder plus de cache fichier chaud. Un swap non nul n’est pas automatiquement mauvais. Ce qui compte, ce sont les taux d’échange actifs (swap-in/out) et l’impact sur la latence.

7) Qu’est‑ce qui cause généralement l’explosion de la mémoire slab ?

Le turnover des métadonnées du système de fichiers (dentries/inodes), les structures de tracking réseau et parfois des bugs du noyau. slabtop vous indique quel cache croît ; cela pointe vers le sous‑système concerné.

8) Mon conteneur montre une faible utilisation mémoire à l’intérieur, mais l’hôte dit que c’est énorme. Qui a raison ?

Ils peuvent avoir tous les deux « raison » parce qu’ils rapportent des périmètres et métriques différents. Pour déboguer un conteneur, faites confiance aux fichiers cgroup : memory.current et memory.stat. Pour le debug hôte, utilisez meminfo plus le PSS sur les processus.

9) Comment détecter rapidement une fuite mémoire sans outils de profilage ?

Recherchez une croissance monotone du Private_Dirty (via /proc/PID/smaps_rollup) et une augmentation du PSS dans le temps, corrélée à l’uptime ou aux requêtes. Mitigez par restart/rollback et implémentez un cap si possible.

10) Est‑il parfois correct d’ajouter plus de RAM ?

Oui — quand le jeu de travail est légitimement plus grand que la machine et que le coût d’optimisation dépasse le coût de la RAM. Mais confirmez avec le PSS et les signaux de pression d’abord ; ne payez pas le matériel pour couvrir une fuite.

Conclusion : prochaines étapes qui réduisent réellement les pages

Quand vous essayez de répondre à « qui mange la RAM », ne fixez pas le regard sur un seul nombre. Utilisez une séquence qui force la vérité : signaux de pression, répartition par seau, puis attribution avec la bonne métrique (PSS), puis périmètre (cgroups vs hôte), puis mémoire noyau si l’espace utilisateur n’explique pas tout.

Actions pratiques :

  • Mettez à jour les alertes pour prioriser MemAvailable, l’activité de swap et les kills OOM de cgroup — pas le « used » brut.
  • Ajoutez un script léger de snapshot mémoire à votre trousse d’incident (meminfo + top PSS + stats cgroup + résumé slab).
  • Dimensionnez correctement les limites mémoire en vous basant sur memory.peak et des baselines PSS, puis ajoutez de la marge.
  • Pour les récidivistes : implémentez des caps pour les caches applicatifs, et considérez toute croissance monotone du private dirty comme une régression jusqu’à preuve du contraire.

Si vous ne faites rien d’autre : la prochaine fois qu’on vous alerte, lisez /proc/meminfo avant de formuler une opinion. Il est plus difficile de discuter avec le noyau quand il vous donne des postes de dépense détaillés.

← Précédent
Désastre de redimensionnement de partition : annuler et redémarrer
Suivant →
DNS : votre domaine fonctionne… jusqu’à ce qu’il cesse — le piège de la délégation expliqué

Laisser un commentaire