Le log du problème

Pour commencer, on va passer en revue les informations disponibles dans le fichier /var/log/syslog/kernel.log (j'ai déjà enlevé les parties inintéressantes):

Free pages:       14732kB (1536kB HighMem)
Active:219399 inactive:14723 dirty:0 writeback:0 unstable:0 free:3683 slab:3741 mapped:214946 pagetables:871
DMA free:12516kB min:16kB low:32kB high:48kB active:0kB inactive:0kB present:16384kB pages_scanned:331 all_unreclaimable? yes
protections[]: 0 0 0
Normal free:680kB min:928kB low:1856kB high:2784kB active:236kB inactive:148kB present:901120kB pages_scanned:544 all_unreclaimable? yes
protections[]: 0 0 0
HighMem free:1536kB min:512kB low:1024kB high:1536kB active:877360kB inactive:58744kB present:1179648kB pages_scanned:0 all_unreclaimable? no
protections[]: 0 0 0
DMA: 5*4kB 4*8kB 3*16kB 2*32kB 3*64kB 1*128kB 1*256kB 1*512kB 1*1024kB 1*2048kB 2*4096kB = 12516kB
Normal: 0*4kB 1*8kB 2*16kB 0*32kB 0*64kB 1*128kB 0*256kB 1*512kB 0*1024kB 0*2048kB 0*4096kB = 680kB
HighMem: 0*4kB 2*8kB 3*16kB 0*32kB 1*64kB 3*128kB 2*256kB 1*512kB 0*1024kB 0*2048kB 0*4096kB = 1536kB
Swap cache: add 228354, delete 228254, find 68953/77275, race 0+4
0 bounce buffer pages
Free swap:       2062584kB
524288 pages of RAM
294896 pages of HIGHMEM
5620 reserved pages
12977 pages shared
100 pages swap cached
Out of Memory: Killed process 24620 (java).

Comme ça, ça fait peur.

Et ce qui est surtout bizarre, c'est qu'il semble qu'il y a encore de la mémoire ! Ce qui est confirmé par la mention :

HighMem free:1536kB

et surtout :

Free swap:       2062584kB

Effectivement, de la mémoire haute et plein de mémoire virtuelle, même si non-performante, sont disponibles. Mais alors, pourquoi l'OOM Killer se met-il en route ?

La mémoire sous Linux

C'est assez compliqué, je vais essayer de simplifier. J'espère que les puristes me pardonneront et/ou me corrigeront le cas échéant.

Pour des raisons que je n'expliquerai pas ici, mais que vous pourrez lire en consultant les liens marqués plus bas, dans tout système en 32 bits qui contient plus de 1 Go de mémoire, la mémoire physique est divisée en deux parties : la partie basse (LOWMEM) et la partie haute (HIGHMEM). On peut voir leurs états avec la commande free :

[root@localhost ~]# free -lm
             total       used       free     shared    buffers     cached
Mem:          2026       1106        919          0         12         92
Low:           874        218        655
High:         1151        887        264
-/+ buffers/cache:       1001       1024
Swap:         2015        377       1638

Voici les points-clés concernant ces zones :

  • Certains travaux ne peuvent être effectués dans la partie haute
  • Le noyau a besoin de la partie basse pour adresser la partie haute
  • L'OOM Killer se met en route s'il n'y a plus de mémoire disponible dans l'une de ces parties

Retour au problème

C'est bien ce dernier point qu'on voit dans le syslog :

Normal free:680kB min:928kB low:1856kB high:2784kB active:236kB inactive:148kB present:901120kB pages_scanned:544 all_unreclaimable? yes

La mémoire disponible (680ko) est inférieure à la valeur minimum configurée (928ko) !

Une solution

La solution que j'ai mise en oeuvre a été de demander gentiment au noyau de garder de la zone basse de mémoire, de manière plus agressive. Cela se fait par exemple avec la commande suivante :

# echo "250" > /proc/sys/vm/lower_zone_protection

Ou en mettant la ligne suivante dans /etc/sysctl.conf:

vm.lower_zone_protection = 250

Évidemment une meilleure solution aurait été de passer à un noyau récent. On fait pas toujours ce qu'on veut.

Documentation