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 -het concentrez-vous suravailable, pas surused. - 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
dmesgpour les journaux de l’OOM killer et quel cgroup l’a déclenché. - Consultez
/proc/meminfopour 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 (
smemou/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 sectionSlabdans 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_Dirtyd’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)
- Exécuter
free -h. Siavailableest bas, poursuivre ; sinon, valider le swap et les cgroups quand même. - Exécuter
vmstat 1 5. Sisi/sosont persistants non nuls, considérer comme vraie pression mémoire. - Faire le grep meminfo. Décider : anonim-heavy vs cache-heavy vs slab-heavy.
- Vérifier
dmesgpour OOM. Si c’est un OOM cgroup, s’arrêter et pivoter vers les cgroups. - Lister les top RSS avec
ps; identifier des candidats. - Valider les candidats avec
smaps_rollup(PSS/private dirty). - Si conteneurs/unités systemd impliqués : lire
memory.current,memory.max,memory.eventsetmemory.stat. - Si les processus n’expliquent pas tout : investiguer le slab avec
slabtop. - Vérifier l’usage tmpfs (
df -hTpour tmpfs/shm). - 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)
- Capturer un snapshot mémoire : meminfo, liste top PSS, stats cgroup, résumé slabtop.
- Ajouter des alertes qui reflètent la réalité : MemAvailable, I/O de swap, kills OOM de cgroup et croissance du slab.
- Fixer des budgets : budgets mémoire par service et les tester sous une concurrence réaliste.
- 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)
- Le champ
MemAvailableest 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. - 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.
- 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.
- 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.
- Le RSS peut surcompter les pages partagées. C’est pourquoi le PSS existe : attribuer les pages partagées plus équitablement entre les processus.
- tmpfs est basé sur la RAM (et le swap). Stocker des « fichiers temporaires » en tmpfs peut devenir une véritable pression mémoire « permanente ».
- 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.
- 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.peaket 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.