Cet article explique comment le oom killer linux intervient en cas de problèmes aigus de mémoire vive et pourquoi il arrête brutalement certains services afin de maintenir le serveur opérationnel. Je vais vous montrer, étape par étape, comment identifier les déclencheurs, comprendre le système de notation et contrôler le comportement du serveur en cas de pression sur la mémoire grâce à des réglages ciblés.
Points centraux
- Déclencheur: Les événements OOM surviennent lorsque la mémoire vive est pleine, que l'espace d'échange est épuisé et que les tentatives de récupération ont échoué.
- Notation: Le noyau attribue un
oom_scoreet tue les processus qui occupent une grande partie de la mémoire. - Reconnaissance: Vous trouverez des informations à ce sujet dans
dmesgavec les messages „ Out of memory “ et „ Killed process “. - Contrôle: Avec
oom_score_adjJe classe les services par ordre de priorité en fonction de leur importance. - Prévention: La surveillance, les limites, la stratégie d'échange et l'analyse des fuites permettent d'éviter les éliminations brutales.
Qu'est-ce que l'OOM Killer dans le noyau Linux ?
L'OOM Killer est la dernière Ligne de sécurité du noyau et met fin aux processus lorsqu’il n’y a plus de pages mémoire disponibles. Je le considère comme un arrêt d’urgence contrôlé qui empêche une panne totale et libère immédiatement de la mémoire vive. Auparavant, le système tente de transférer des pages vers le disque ou de vider les caches, mais si la pression persiste, il ne reste plus d’autre solution que cette coupure brutale. Dans les journaux, je reconnais ce moment à des entrées indiquant „ Out of memory “ et „ Killed process “, souvent accompagnées d’un SIGKILL. Ce comportement est activé par défaut et s’exécute automatiquement, ce qui provoque souvent des surprises dans les environnements de production lorsque des services importants disparaissent sans avertissement.
Quand ce mécanisme entre-t-il en action ?
Des événements OOM se produisent lorsque la mémoire vive physique est presque entièrement occupée, que l'espace d'échange ne fournit plus de tampon et que les demandes de nouvelles pages échouent. Dans cette situation, le noyau évalue s'il est possible de récupérer de la mémoire à partir du Cache de page et que le swap est encore efficace, et ne lance le « killer » que lorsque la situation est désespérée. Les pics de courte durée ne déclenchent pas directement ce mécanisme ; il faut une pression soutenue et des tentatives de libération infructueuses. Pour l’analyse, il m’est utile d’examiner le RSS, l’utilisation de la mémoire swap, la part du cache de page et les allocations par groupe de contrôle. Ceux qui souhaitent mieux comprendre le fonctionnement de l’éviction du cache peuvent consulter les informations de fond sur Éviction du cache de page les examiner et mesurer les effets sur son propre système.
Comment le noyau choisit-il la „ victime “ ?
La sélection suit un Notation, qui utilise Linux comme oom_score calculé par processus. Une part importante de la mémoire totale, un volume RSS élevé et un rôle non critique entraînent un score plus élevé. Les processus liés au système, tels que « init », bénéficient d'une réduction, tandis que les processus « worker » gourmands en mémoire ou les caches occupent souvent les premières places. Via oom_score_adj je peux modifier le score de manière ciblée et ainsi contrôler la chaîne des victimes. En général, le noyau termine le processus ayant le score le plus élevé à l'aide d'un SIGKILL afin de libérer d'un seul coup le plus de mémoire vive possible.
Identifier les indices : journaux et signaux
Après une fin de service imprévue, je commence par vérifier dmesg et les journaux du noyau. Si j’y trouve des messages „ Out of memory “ et „ Killed process “, je note le PID, le nom du processus, l’utilisateur et le score calculé. Souvent, l’application elle-même ne dispose pas de journal d’erreurs, car SIGKILL ne permet pas de phase de nettoyage. Je recoupe ces informations avec les données de surveillance afin de retracer l’augmentation de la RAM, de l’espace d’échange et de la mémoire RSS par processus. Cela me permet d’identifier de manière fiable et rapide les fuites, les tas de mémoire trop volumineux ou les limites manquantes.
Prendre le contrôle avec oom_score et oom_score_adj
Chaque processus possède, dans /proc/[PID]/oom_score un dernier Valeur compte tenu de sa situation de vulnérabilité. Avec /proc/[PID]/oom_score_adj Je réduis ou j'augmente ainsi la probabilité que le noyau mette fin à ce processus. Je protège les services critiques, tels que les bases de données, à l'aide d'un adj négatif, tandis que je rends les workers non essentiels „ sacrifiables “ grâce à un adj positif. La modification prend effet immédiatement, ce qui s'avère particulièrement utile lors de déploiements ou de tests de charge. Je transforme ainsi un mécanisme d’urgence imprévisible en un outil qui respecte mes priorités.
Scénarios d'hébergement typiques soumis à des contraintes de stockage
Dans les environnements comportant de nombreux conteneurs, bases de données et caches, je constate très souvent l'intervention de l'OOM Killer. Une base de données qui grossit de manière incontrôlée évince d'autres services de la RAM et entraîne des plantages brutaux. Les fuites de mémoire dans les applications web s'accumulent au fil des heures, jusqu'à ce que même le récupérateur de mémoire ne puisse plus rien y faire. Des limites de conteneurs trop généreuses sur un hôte trop petit aggravent encore la situation. Ceux qui connaissent ces schémas mettent en place des alertes à temps et interviennent avant que le « tueur » ne prenne le dessus.
Bonnes pratiques pour éviter les « hard kills »
Je planifie la mémoire de manière réaliste, je prévois une marge pour les pics de charge et je fixe des limites claires pour chaque service. Le système de surveillance enregistre, pour chaque processus, le RSS, l'utilisation de la mémoire swap et oom_score, afin que les alertes se déclenchent avant qu'une situation critique ne survienne. Dans les configurations de conteneurs, je définis des limites de cgroups afin d’empêcher certains services de monopoliser les ressources de l’hôte. Une stratégie de swap bien pensée permet de gérer les pics de charge sans ralentir le système de manière permanente. Pour approfondir ma compréhension et faciliter la planification, je m’appuie sur des guides pratiques concernant Gérer la mémoire virtuelle, afin que les charges de travail disposent d'une capacité suffisante.
Procédure structurée en cas d'incident OOM
Après l'incident, je commence par extraire les lignes de journal dmesg et kern.log, puis je les classe par ordre chronologique. Dans l'outil de surveillance, je vérifie les courbes de la RAM, de l'espace d'échange, du RSS et du cache de page afin d'observer l'évolution de la charge. Ensuite, je vérifie les limites ulimits, les limites des Cgroups et des conteneurs, ainsi que les paramètres des applications, tels que les tas des JVM. Puis j'ajuste oom_score_adj afin de privilégier l'essentiel et d'éliminer d'abord le superflu. Pour finir, je m'attaque à la cause : je corrige les fuites, je limite les caches, je réduis le parallélisme et je dimensionne correctement les capacités.
Particularités des environnements VPS et cloud
Sur les machines virtuelles, un deuxième niveau de limites s'ajoute, par exemple via l'hyperviseur ou l'orchestration. Je connais donc la mémoire allouée RAM- Définissez précisément la quantité et configurez les limites Kubernetes ou des conteneurs en conséquence. Le « OOM Killer » de Linux continue de fonctionner comme décrit, mais les mécanismes propres au fournisseur peuvent déclencher des limitations supplémentaires. Une hiérarchisation claire s’avère particulièrement utile lorsque le nombre de pods est élevé : les déploiements importants bénéficient de réserves, tandis que les tâches non critiques sont soumises à des contraintes plus strictes. La documentation fournie par le fournisseur de la plateforme et vos propres tests permettent d’éviter les surprises en production.
Réglage fin de la mémoire : overcommit, swappiness et caches
Pour maîtriser les risques liés à l'OOM, il faut ajuster les paramètres du noyau avec soin et comprendre leurs interactions. vm.overcommit_memory et vm.overcommit_ratio contrôler le niveau de générosité avec lequel Linux autorise les allocations virtuelles, tandis que vm.swappiness influence le rapport entre le swapping et le reclaim. vm.vfs_cache_pressure régle l'agressivité lors de la libération des caches inode et dentry, ce qui a un impact direct sur la marge de manœuvre du cache de pagination. Je teste toujours les effets dans des conditions réalistes Dernier, enregistrez les métriques et procédez à des modifications progressives. Pour en savoir plus sur le contexte et les scénarios, consultez Surallocation de mémoire, afin de choisir judicieusement ses propres paramètres par défaut.
| Paramètre/indicateur | Rôle dans le système | Où passer l'examen | Direction habituelle |
|---|---|---|---|
| RAM libre | Une marge de sécurité contre les éliminations brutales | free, /proc/meminfo | Disposer d'une réserve suffisante |
| Utilisation de la mémoire swap | Amortisseurs pour pics | free, vmstat | Faible à modéré |
| vm.overcommit_memory | Attribution virtuelle | sysctl | 0/2 selon le risque |
| vm.overcommit_ratio | Quota pour l'overcommit | sysctl | Adapté à la charge de travail |
| vm.swappiness | Tendance au swap | sysctl | La moyenne plutôt que l'extrême |
| vm.vfs_cache_pressure | Récupération des caches VFS | sysctl | 100 comme point de départ |
OOM global vs. OOM de Cgroup : qu'est-ce qui s'arrête exactement ?
Dans les configurations modernes utilisant les Cgroups (v1/v2), un événement OOM peut local dans un cgroup de mémoire ou global être déclenché sur l'hôte. Si un processus s'exécute dans un conteneur avec un mode strict memory.max (ou limite), le noyau ne met généralement fin qu'aux processus de ce Cgroup („ memcg OOM “), tandis que le système dans son ensemble continue de fonctionner. Dans dmesg Je le reconnais à des indices tels que constraint=CONSTRAINT_MEMCG ou des indications concernant le Cgroup concerné. Ce n'est que lorsqu'aucun Cgroup ne peut plus céder de RAM et que la mémoire globale est épuisée que le à l'échelle du système OOM Killer. Pour garantir la stabilité, il est important pour moi de définir les limites de manière à ce qu'un service trop gourmand en ressources échoue au sein de son Cgroup, plutôt que d'entraîner l'ensemble de l'hôte dans sa chute. Dans Cgroups v2, je peux également utiliser memory.high définir des limitations douces et avec memory.oom.group préciser qu'en cas d'urgence, l'ensemble du groupe s'arrête – c'est plus propre qu'un processus résiduel qui continue de fonctionner à moitié.
Outils et indicateurs dans la pratique
Pour identifier rapidement les causes, je recueille des données chiffrées reproductibles. Ces outils m'aident régulièrement :
- Aperçu du processus:
ps -eo pid,ppid,cmd,%mem,rss --sort=-rss | headaffiche les applications qui consomment le plus de mémoire. - Rollup Smaps:
cat /proc//smaps_rollupfournit les valeurs RSS/PSS/Swap d'un processus sans analyse fastidieuse. - pmap:
pmap -x | sort -nrk3 | headrépertorie les mappages avec leur taille et leur flux RSS, ce qui est utile pour les tas et les segments volumineux. - Utilisation des dalles:
slabtop -oaffiche les caches du noyau, qui peuvent gonfler en cas de charge élevée. - Pression du système:
vmstat 1etsar -r 1fournissent des informations contextuelles sur la pagination, les E/S de swap et les libérations de mémoire. - Statistiques des cgroups: Dans la version 2, je vérifie
/sys/fs/cgroup/memory.current,memory.swap.currentetmemory.statdu service concerné.
Lire facilement les traces OOM de #
dmesg -T | egrep -i 'out of memory|oom-kill|killed process'
# Trier les meilleurs candidats selon leur oom_score
for p in /proc/[0-9]*; do
pid=${p##*/}
[ -r "$p/oom_score" ] || continue
printf "%6s %5s %-30s\n" \
"$(cat $p/oom_score)" \
"$(cat $p/oom_score_adj 2>/dev/null || echo 0)" \
"$(tr -d '\0' < $p/comm)"
done | sort -nr | head -n 20
Si je constate des OOM à plusieurs reprises, je consigne les Ligne de base ces valeurs en fonctionnement normal et compare-les avec la fenêtre d'incident. Les écarts sautent immédiatement aux yeux, comme une hausse incontrôlée du PSS ou des « slabs » disproportionnés.
Systemd, conteneurs et orchestration : un contrôle ciblé
Sous systemd, je définis les priorités et les limites déclaré dans les fichiers Unit :
[Service]
# Protéger le processus ou le rendre sacrifiable
OOMScoreAdjust=-900
# Limites de mémoire strictes/souples (cgroup v2)
MemoryMax=8G
MemoryHigh=6G
# Facultatif : limiter l'espace d'échange
MemorySwapMax=2G
# Comportement en cas d'OOM sous systemd
# (par ex. forcer le redémarrage)
Restart=on-failure
RestartSec=5
Dans les environnements de conteneurs, je veille à définir des limites claires pour chaque service. Il est important pour moi de faire la distinction entre Demande (réservation prévue) et Limite (limite maximale stricte). Les pods dont les requêtes et les limites sont conformes bénéficient d'un meilleur classement en termes de qualité de service (QoS) ; les charges de travail „ BestEffort “ sont exposées au risque d'OOM. Détail pratique : lorsque le noyau arrête un conteneur en raison d'un OOM de Cgroup, j'observe souvent un code de sortie 137 et les événements avec OOMKilled; dans l'hôte‑dmesg Il est possible d'établir une corrélation. Dans les clusters productifs, je planifie les déploiements critiques en mode „ Guaranteed “, tandis que les tâches par lots s'exécutent délibérément dans des délais plus courts et cèdent ainsi la priorité en premier.
Détails du noyau : OOM Reaper, THP et fragmentation
Après avoir tué sa proie, le OOM Reaper: un thread du noyau supprime le mappage mémoire du processus victime aussi rapidement que possible afin que la mémoire vive soit réellement libérée. Cela explique pourquoi la mémoire n'est parfois libérée qu'après selon réapparaît visiblement à l'entrée du canal. Parallèlement, la Compactage de la mémoire atteindre ses limites : si la mémoire vive est fortement fragmentée, il manque des blocs contigus pour les allocations de grande taille (par exemple avec les Transparent Huge Pages, THP). Les THP offrent de bonnes performances, mais peuvent compliquer les allocations en cas de forte charge. Pour les charges de travail où la latence est critique, je désactive ou limite les THP à titre d’essai et j’en mesure les effets.
Un autre facteur est Caches de slab et le cache de pages : dans le cadre de charges de travail à forte intensité d'E/S, ces caches augmentent considérablement. Avec vm.vfs_cache_pressure et grâce à un nettoyage ciblé, il est possible d'en contrôler la taille ; je n'utilise la vidange systématique (Drop-Caches) qu'à titre d'outil de diagnostic, et non comme solution à long terme. De plus, je veille à NUMA: Si un nœud de mémoire est saturé, un processus dans cette zone NUMA peut échouer même s'il reste de la mémoire RAM disponible au niveau global. Des messages correspondants apparaissent également dans les journaux du noyau.
Approfondir les stratégies de swap : Swappiness, ZRAM/Zswap, budget d'E/S
Le swap n'est pas un mal, mais un amortisseurs. L'essentiel est de l'utiliser à bon escient. Avec vm.swappiness Je règle ainsi le moment où le noyau commence à utiliser l'espace d'échange. Des valeurs trop faibles favorisent la prédominance du cache de pages et peuvent déclencher plus tôt des erreurs OOM ; des valeurs trop élevées génèrent une charge excessive sur les E/S de l'espace d'échange et ralentissent le système. Sur les hôtes compacts, j'utilise volontiers ZRAM ou Zswap, afin de créer une mémoire tampon compressée qui absorbe les pics sans surcharger le disque. Il est important de retenir que la mémoire swap ne remplace pas la capacité manquante. Elle ne fait que gagner du temps pour éviter que le « OOM killer » n'ait à intervenir.
Cas particuliers : mlock, RLIMITS, les pièges de l'overcommit
Certaines conditions limites accentuent les risques d'OOM ou modifient le comportement :
- Mémoire verrouillée: Processus qui, via
mlock()En épinglant des pages, vous les soustrayez au Reclaim. À un taux élevé, cela peut ralentir la vitesse du Reaper. - RLimits:
RLIMIT_ASetRLIMIT_RSSfixent des limites maximales par processus et empêchent certains services de prendre trop d'espace – un élément de lutte contre les erreurs OOM. - Overcommit: Des paramètres d'overcommit trop généreux permettent de disposer d'un grand espace d'adressage virtuel qui ne pourra pas être couvert physiquement par la suite. Les pics d'allocation liés à la présence simultanée de nombreux threads entraînent alors des erreurs d'accès et accélèrent les événements OOM.
- panic_on_oom: Pour les systèmes hautement critiques, il est possible de déclencher une « kernel panic » en cas d'OOM. Cette option n'est pertinente que dans des scénarios de haute disponibilité (HA) strictement définis ; dans les autres cas, elle est contre-productive.
- „ Incassable “ : un choix risqué:
oom_score_adj=-1000Cela protège certes contre le « killer », mais peut bloquer l'ensemble du système. Je ne l'utilise que pour les petits processus absolument essentiels (par exemple, init), et non pour les services serveur gourmands en mémoire.
Dans la pratique : définir des priorités et garantir la mise en œuvre des changements
Au sein de l'équipe, je définis une Classement des services : que faut-il conserver, que peut-on supprimer en premier ? Je traduis cet ordre en oom_score_adj, les limites des Cgroups et (le cas échéant) les politiques de redémarrage. Les modifications sont intégrées sous forme de code dans les manifestes d’unité ou de déploiement, accompagnées de points de mesure dans le système de surveillance. Lors des tests de charge, je simule une pression sur la mémoire : j’augmente les volumes de données, j’intensifie le parallélisme, je laisse les caches se remplir – et j’observe si ce sont bien les processus „ sacrifiables “ qui tombent, tandis que les composants essentiels restent en ligne. Ce n’est que lorsque cela fonctionne de manière reproductible que la configuration est mise en production.
Modèles de diagnostic : distinguer les fuites, les tas et la fragmentation
Toutes les hausses du RSS ne constituent pas forcément une fuite. Je fais systématiquement la distinction suivante :
- fuite: Les valeurs RSS/PSS augmentent de manière monotone, même sans augmentation de la charge ;
smaps_rollupaugmente de manière régulière ; les cycles GC (dans les environnements d'exécution gérés) n'y changent rien. - Pics de mémoire dynamique: Le RSS augmente avec la charge puis redescend ; le cache de page est en corrélation avec les modèles d'E/S.
- Fragmentation: La mémoire vive disponible est suffisante, mais les allocations importantes échouent ; les journaux indiquent des tentatives de compactage, les allocations THP échouent plus souvent.
Pour les charges de travail JVM ou Node, je vérifie si le runtime détecte les limites des conteneurs. Des tas (heaps) ou des caches de code JIT surdimensionnés peuvent dépasser les limites prévues et provoquer des erreurs OOM, même s’il semble encore y avoir de la marge. Je configure les tas, surcharge comprise, de manière à ce que, sous MemoryMax il reste encore de la marge pour les parts natives, les piles de threads et le cache de pages.
Guide pratique pour les déploiements et les tests de charge en cas de pression sur le stockage
- Mesurer la valeur de référence: RSS/PSS par service, parts de slab, taux de swap, tailles de cache,
oom_score. - Fixer des limites: Mémoire haute/max. ou définir des limites de conteneurs avec une marge de sécurité réaliste ;
OOMScoreAdjustattribuées par ordre de priorité. - Provoquer du stress: volume de données, concurrence, croissance du cache ; noter les profils d'E/S et de CPU.
- Observer:
dmesg -T, les métriques de l'hôte et du cgroup ; vérifier lequel des deux est le premier à être mis sous pression. - Itérer: Ajuster les limites/paramètres adjacents, modifier le paramètre « swappiness », tester les paramètres THP, puis effectuer une nouvelle mesure.
- Automatiser: Intégrer des contrôles dans le processus CI/CD, mettre en place des alertes en cas de dépassement des seuils, définir des politiques de redémarrage pour les services affectés.
En résumé : des mesures concrètes
Je comprends le « OOM Killer » comme Signal, que mon système manquait auparavant de marge de manœuvre ou que les processus n'étaient pas correctement hiérarchisés. Grâce à la surveillance, à des limites réalistes, à une stratégie d'échange bien pensée et à une utilisation réfléchie de oom_score_adj Je réduis considérablement les arrêts brutaux. Dans les environnements de production, je protège les processus essentiels, je rends les services secondaires superflus et j'évalue chaque modification. Pour les conteneurs, je définis strictement les limites des Cgroups afin qu’un service ne bloque pas l’ensemble du système hôte. En respectant cette discipline, on préserve la réactivité de Linux même sous pression et on réduit considérablement le temps nécessaire à l’identification de la cause.


