Aller au contenu
  • Accueil
  • Infrastructure
  • Datacenter
  • Services
    Nos services
    InfogéranceCloudHébergementIABare MetalHousingTransit IP
  • Blog
Contactez-nous
  1. Accueil
  2. /
  3. Blog
  4. /
  5. OOM killer Linux : lire le dump pour trouver qui a rempli la mémoire

Linux
Performance
Administration

OOM killer Linux : lire le dump pour trouver qui a rempli la mémoire

13 novembre 2026

11 min de lecture

Sommaire
Ce que le log dit avant le tableau
pid, uid, tgid : qui est qui
total_vm : on passe
rss : le chiffre qu'on lit mal
rss_anon, rss_file, rss_shmem : le détail, depuis le noyau 6.8
pgtables_bytes : la seule colonne en octets
swapents : la mémoire partie dans le swap
oom_score_adj : pourquoi ce n'est pas toujours le plus gros
name : additionner par service
Apache en prod : 150 workers, 8,6 Go de RSS sur une machine de 6 Go
Trois kills en neuf jours, sonde au vert
Sources
[   18.581465] pool invoked oom-killer: gfp_mask=0x140dca(GFP_HIGHUSER_MOVABLE|__GFP_COMP|__GFP_ZERO), order=0, oom_score_adj=0
[   18.583942] Free swap  = 0kB
[   18.583951] Total swap = 131068kB
[   18.584047] Tasks state (memory values in pages):
[   18.584056] [  pid  ]   uid  tgid total_vm      rss rss_anon rss_file rss_shmem pgtables_bytes swapents oom_score_adj name
[   18.584139] [    142]     0   142    20704    19054    13856        0      5198   204800     1504             0 pool
[   18.584170] [    143]     0   143    30944       96       32        0        64   286720    30688             0 base
[   18.584186] [    145]     0   145    24544    17770    17706        0        64   196608     1536             0 pool
[   18.584566] [    170]     0   170    24544    14294    14253        0        41   172032     1527             0 pool
[   18.584593] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/,task=base,pid=143,uid=0
[   18.585151] Out of memory: Killed process 143 (base) total-vm:123776kB, anon-rss:128kB, file-rss:0kB, shmem-rss:256kB, UID:0 pgtables:280kB oom_score_adj:0

Le noyau vient de tuer un processus qui n'occupe que 128 Ko de mémoire anonyme en RAM. Ce log sort d'une reproduction en machine virtuelle, sur le noyau Debian 6.12 : 465 Mio de RAM, 128 Mio de swap, des noms de processus neutres, 23 lignes pool coupées.

Pour trouver qui a rempli la mémoire, on lit le tableau Tasks state du dump et on additionne rss et swapents par nom de processus. La ligne Killed process désigne la victime, pas forcément la cause. Et le rss n'est pas de la mémoire privée : il compte les pages partagées dans chaque processus qui les mappe, donc la somme par service classe les suspects sans donner un besoin en mémoire.

Ce que le log dit avant le tableau

pool invoked oom-killer nomme la tâche qui demandait une page au moment où le noyau a renoncé. C'est la dernière arrivée, et ça ne prouve rien contre elle.

Free swap = 0kB : RAM et swap sont tombés ensemble. Le rôle et le dimensionnement du swap ont leur propre article.

constraint=CONSTRAINT_NONE et global_oom indiquent un manque de mémoire de toute la machine. Le tableau liste alors tous les processus. Sur un dépassement de limite de cgroup, le message devient Memory cgroup out of memory, la contrainte CONSTRAINT_MEMCG, et dump_tasks ne parcourt que les tâches de ce cgroup. Le coupable peut n'avoir aucun voisin dans le tableau, et une limite MemoryMax posée par systemd peut tuer un service sur une machine qui a encore de la RAM libre.

Ne comptez pas sur un tableau à chaque kill. Dans mm/oom_kill.c, l'en-tête et le tableau passent par une limite de débit, dix par tranche de cinq secondes, alors que la ligne Killed process s'écrit toujours. Une rafale de kills peut laisser des lignes de kill sans tableau. Le réglage vm.oom_dump_tasks vaut 1 par défaut ; à 0, le tableau disparaît.

pid, uid, tgid : qui est qui

Une ligne par processus, pas par thread, sans les threads du noyau ni le PID 1. Le tgid identifie le processus. Le pid affiché peut être celui d'un autre de ses threads si le premier a déjà rendu sa mémoire.

Le uid devient utile quand le nom ne suffit pas : deux services sur le même binaire tombent sous le même name, et il les sépare s'ils tournent sous des comptes distincts.

total_vm : on passe

C'est la taille de l'espace d'adressage, pas la mémoire occupée. Elle sert à une seule chose, vérifier l'unité. La ligne de kill donne total-vm:123776kB, le tableau donne 30944 pages pour le même processus : 123776 divisé par 30944 fait 4, donc des pages de 4 Kio sur cette machine.

rss : le chiffre qu'on lit mal

Le tableau affiche get_mm_rss(), et le code de cette fonction est court :

static inline unsigned long get_mm_rss(struct mm_struct *mm)
{
	return get_mm_counter(mm, MM_FILEPAGES) +
		get_mm_counter(mm, MM_ANONPAGES) +
		get_mm_counter(mm, MM_SHMEMPAGES);
}

Ce sont des compteurs par processus. Une page partagée par 26 processus entre dans les 26 compteurs. La reproduction le montre : 1 792 Mio de rss cumulés pour les 26 pool, sur une machine de 465 Mio. Ce serait impossible si rss était de la mémoire privée.

Cette somme ne mesure pas un besoin en mémoire. Elle peut aussi tromper dans l'autre sens : le rss exclut le swap, comme le montre la ligne base, qui ne pèse plus que 96 pages en RAM.

rss_anon, rss_file, rss_shmem : le détail, depuis le noyau 6.8

Ces trois colonnes détaillent le rss et s'additionnent exactement : 17706 + 0 + 64 = 17770 sur la ligne 145. Absentes du code de la v6.7, elles sont présentes dans la v6.8, la 6.12 et la branche principale. Debian 12, en 6.1, garde l'ancien format, relevé sur notre reproduction :

[  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name

La tentation est de retirer rss_file et rss_shmem pour obtenir le privé. Ça ne marche pas avec un serveur à préchargement. Nos pool héritent de leur parent 60 Mio anonymes par copie à l'écriture : ces pages restent communes, et pourtant elles tombent dans rss_anon de chaque enfant, comme le montrent les 17706 pages de la ligne 145 (69 Mio). rss_anon n'est donc pas de la mémoire privée non plus. Un Apache prefork qui charge PHP avant de se dupliquer fonctionne pareil.

pgtables_bytes : la seule colonne en octets

L'en-tête annonce des pages, mais cette colonne est en octets. Elle compte les tables de pages du processus, 196608 octets sur la ligne 145. Négligeable ligne à ligne, elle entre quand même dans le score de sélection.

swapents : la mémoire partie dans le swap

En pages. La victime du début s'explique ici : base a 96 pages en RAM et 30688 dans le swap, soit 120 Mio qu'une lecture du seul rss ne voit pas. Lisez toujours les deux colonnes ensemble.

Le score de sélection le confirme. oom_badness() additionne rss, swapents et pgtables_bytes divisé par la taille de page. Pour base, 96 + 30688 + 70 = 30854. Pour le plus gros pool, ligne 142, 19054 + 1504 + 50 = 20608.

La plus petite ligne en RAM est bien le plus gros score. Le noyau a tué celui qui lui rendrait le plus, pas celui qui a causé le manque. base avait alloué ses 120 Mio une fois pour toutes ; ce sont les pool, créés un par un, qui ont vidé la machine.

La colonne surcompte elle aussi : sur la reproduction en noyau 6.1, les swapents des 29 pool totalisent 1 001 Mio pour un swap de 128 Mio. Une page partagée partie dans le swap compte dans chaque processus qui la référence.

oom_score_adj : pourquoi ce n'est pas toujours le plus gros

Cette colonne vaut 0 partout dans notre log, donc le score est resté brut. Sa plage va de -1000 à +1000, et oom_badness() l'ajoute au score après l'avoir multipliée par un millième de la mémoire autorisée (RAM plus swap sur un OOM global, la limite sur un cgroup). À -1000, le processus n'est plus éligible du tout. Quand la victime n'est pas celle que les trois colonnes désignent, regardez ici en premier.

name : additionner par service

name est tronqué à 15 caractères (TASK_COMM_LEN vaut 16, zéro final compris). Le script lit l'en-tête et marche sur les deux formats. Une lecture par position depuis la fin tombe sur rss_shmem au lieu de rss sur un noyau récent.

journalctl -k --since "AAAA-MM-JJ hh:mm" --until "AAAA-MM-JJ hh:mm" \
| awk -v kio=4 '
/\[ +pid +\]/ {
  sub(/^.*\[ +pid +\] +/, "")
  n = split($0, h, " ")
  for (i = 1; i <= n; i++) col[h[i]] = i
  delete nb; delete rss; delete swp
  next
}
col["rss"] && /\[ *[0-9]+\] +[0-9]+ +[0-9]+ / {
  sub(/^.*\[ *[0-9]+\] +/, "")
  nom = $(col["name"])
  nb[nom]++; rss[nom] += $(col["rss"]); swp[nom] += $(col["swapents"])
}
END {
  for (nom in nb)
    printf "%-16s %5d %9.0f %9.0f\n", nom, nb[nom], rss[nom]*kio/1024, swp[nom]*kio/1024
}' | sort -k3 -nr

-k implique le boot courant : après un redémarrage, ajoutez -b -1, ou le kill n'apparaît pas. Le script ne garde que le dernier tableau de la fenêtre : resserrez --since et --until sur un seul kill. kio vaut la taille de page trouvée plus haut.

Sortie sur le dump du début (nom, processus, rss et swap cumulés, en Mio) :

pool                26      1792       153
base                 1         0       120

L'empreinte d'un service est au moins celle de son plus gros processus (rss + swapents), et au plus la somme ci-dessus. Pour pool, cela donne entre 80 et 1 945 Mio, alors que notre programme n'a touché que 455 Mio de données : 80 Mio préchargés plus 15 Mio pour chacun des 25 enfants. La fourchette suffit à montrer que base, plafonné à 120 Mio, n'a pas pu remplir 593 Mio de RAM et de swap seul. Le tableau ignore ce qu'aucun processus ne mappe : si la somme n'explique pas le manque, lisez slab_unreclaimable et shmem dans le bloc Mem-Info d'un OOM global.

Le dump ne donne pas le privé : il se mesure à chaud. /proc/PID/smaps_rollup donne Pss, où chaque page est divisée par le nombre de processus qui la partagent, et Private_Clean plus Private_Dirty pour la mémoire strictement privée (l'USS). Sur un pool de notre programme, relevé avec 10 enfants et avant toute pression :

Rss:               77176 kB
Pss:               20995 kB
Private_Dirty:     15380 kB

Les 15 Mio privés sont ce qu'il a touché lui-même. Sur un Apache avec mod_php à vide en Debian 13, on a relevé 64,7 Mio de rss cumulés pour 21,1 Mio de PSS sur 6 processus. Il faut être root pour lire ces fichiers chez un autre utilisateur, et sous Docker il faut en plus --cap-add SYS_PTRACE. Pour les outils à chaud, voir la gestion des processus.

Apache en prod : 150 workers, 8,6 Go de RSS sur une machine de 6 Go

Un serveur web et sa base sur une machine Debian 12 de 5 925 Mio. Le noyau a tué la base, et le tableau du dump montrait 151 processus apache2, médiane à 61 Mio, maximum à 75 Mio, 8 578 Mio de rss cumulés. Le swap était à 99 %.

Les 8,6 Go surcomptent le partagé et ne disent pas le besoin. Le suspect est dans le nombre de lignes : 151, soit le plafond de 150 plus le parent. Le pool était plein. Or 150 est la valeur de MaxRequestWorkers dans le mpm_prefork.conf de Debian, relevée sur des Debian 12 et 13 fraîches (Apache 2.4.68 et 2.4.69) ; le défaut de la documentation Apache est 256. Apache seul y démarre en event, et sur la 13, installer libapache2-mod-php8.4 le bascule en prefork : chaque requête occupe alors un processus entier.

Le calcul : 5 925 moins 2 000 Mio réservés à la base, aux autres conteneurs et au système, soit 3 925 Mio, divisés par les 75 Mio du pire cas donnent 52 places. On a posé 50, un choix prudent plutôt qu'un résultat exact, car deux biais se croisent : le partagé gonfle ces 75 Mio, le swap les dégonfle. Le test de charge a validé la valeur : 60 requêtes simultanées, compte de processus plafonné à 51, mémoire à 3 194 Mio sur 5 925. En contrepartie, les connexions en excès attendent dans la file au lieu de vider la machine.

Ce calcul ne vaut que pour prefork avec mod_php. Avec PHP-FPM, Apache ne porte plus PHP : la mémoire est dans les enfants php-fpm, plafonnés par pm.max_children, que la documentation PHP donne comme l'équivalent de ce réglage. La même somme par name s'applique, avec le même surcompte des pages partagées.

Trois kills en neuf jours, sonde au vert

Cette machine, on l'exploite chez SHPV. La base y a été tuée trois fois en neuf jours, et la sonde de supervision est restée au vert parce qu'elle lisait une page servie par un cache. Elle interroge désormais la base. Une ligne Killed process dans le journal du noyau mérite sa propre alerte. Et pensez à garder ces journaux assez longtemps : sans le tableau, il ne reste qu'un nom de victime.

Sources

  • mm/oom_kill.c, noyau 6.12.115 : dump_task, dump_tasks, oom_badness, limite de débit du dump et message de kill
  • mm/oom_kill.c, noyau 6.7 : dernier format sans les colonnes détaillées
  • mm/oom_kill.c, noyau 6.8 : arrivée de rss_anon, rss_file et rss_shmem
  • include/linux/mm.h, noyau 6.12.115 : définition de get_mm_rss
  • include/linux/ratelimit_types.h, noyau 6.12.115 : valeurs de la limite de débit
  • Documentation du noyau, sysctl vm : oom_dump_tasks et sa valeur par défaut
  • Documentation du noyau, /proc : PSS, smaps_rollup, plage de oom_score_adj
  • Apache HTTP Server, directives MPM communes : MaxRequestWorkers, défaut de 256 en prefork et mise en file d'attente
  • Manuel PHP, configuration de FPM : pm.max_children et son équivalence avec prefork
  • systemd, journalctl : -k implique le boot courant, options -b, --since et --until

Besoin d'aide sur ce sujet ?

Notre équipe d'experts est là pour vous accompagner dans vos projets d'infrastructure et d'infogérance.

Contactez-nous

Articles similaires

Kernel Linux 2026 : tuning, modules, compilation et paramètres système
Administration
Linux
Performance
Administration
Linux
Performance

Kernel Linux 2026 : tuning, modules, compilation et paramètres système

Guide complet kernel Linux 2026 : sysctl tuning, modules chargement, compilation kernel custom, paramètres boot, optimisation performance.

6 févr. 2026
Lire plus
Gestion processus Linux 2026 : ps, top, htop, kill et systemctl détaillé
Linux
Administration
Performance
Linux
Administration
Performance

Gestion processus Linux 2026 : ps, top, htop, kill et systemctl détaillé

Guide complet gestion processus Linux 2026 : commandes ps, top, htop, kill, nice, systemctl. Monitoring, priorités, signals, debugging et optimisation performance.

26 janv. 2026
Lire plus
Swap Linux 2026 : configuration, swappiness, zswap et optimisation mémoire
Administration
Linux
Performance
Administration
Linux
Performance

Swap Linux 2026 : configuration, swappiness, zswap et optimisation mémoire

Guide complet swap Linux 2026 : créer partition swap, fichier swap, configurer swappiness, zswap, optimisation mémoire RAM. Performance et bonnes pratiques serveur.

24 janv. 2026
Lire plus

SHPV, votre partenaire de confiance en infrastructure et infogérance informatique en France.

SHPV
Contactez-nousNous contacterBlog
Expertise
InfrastructureDatacenterInfogéranceCloudHébergementTransit IPIABare MetalHousing
Légales
Conditions Générales de VenteCPS - Contrat de ServicesCPS - Hébergement CloudCPS - Microsoft 365Accord sous-traitance RGPDTarifs interventions

SHPV © 2026 - Tous droits réservés

Mentions légalesPolitiques de confidentialité
SHPV FRANCE - SAS au capital de 16 000 € - 52 Rue Romain Rolland, 71230 Saint-Vallier - SIRET n°80886287400035 - R.C.S. Chalon-sur-Saône. Par téléphone 09 72 310 818 - Email: support@shpv.fr