Server

The Linux OOM Killer, Explained

By Prabath Thalangama· October 24, 2026· 4 min read
#linux#memory#troubleshooting

Introduction

When Linux can't satisfy a memory allocation and can't reclaim enough, the OOM (out-of-memory) killer picks a process to SIGKILL to free memory. It's a last resort — noisy, blunt, and usually a symptom of a leak or under-provisioning.

How it picks

Each process gets an oom_score (0–1000), roughly proportional to its share of available memory, then adjusted:

cat /proc/<pid>/oom_score            # current score
cat /proc/<pid>/oom_score_adj        # -1000..+1000, your bias
  • Bigger memory footprint → higher score → more likely to be killed.
  • oom_score_adj = -1000 makes a process immune (OOM_SCORE_ADJ_MIN).
  • oom_score_adj = +1000 makes it the preferred victim.
  • The killer avoids killing init, kernel threads, and (if possible) tasks with oom_score_adj very negative.

Protect a critical daemon:

# systemd unit
[Service]
OOMScoreAdjust=-800

Or make a batch job the sacrificial one: OOMScoreAdjust=500.

Reading the dmesg dump

dmesg -T | grep -A30 -i 'killed process\|out of memory\|oom-kill'
journalctl -k | grep -i oom

You'll see something like:

oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=...,mems_allowed=0,
  global_oom,task_memcg=/system.slice/myapp.service,task=python,pid=4242,uid=1000
Out of memory: Killed process 4242 (python) total-vm:8200000kB, anon-rss:7900000kB,
  file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:15600kB oom_score_adj:0

Then a task list table (every process with rss, oom_score_adj) — this shows what else was using memory, which is how you find the real hog if the victim was collateral.

  • constraint=CONSTRAINT_NONE / global_oom → the whole machine ran out.
  • task_memcg=/system.slice/foo.service + memory cgroup out of memory → a cgroup limit was hit (systemd MemoryMax, a container limit), not the host. Only that cgroup is affected.

Overcommit

sysctl vm.overcommit_memory vm.overcommit_ratio
  • 0 (default) — heuristic: allow overcommit unless the request is "obviously" too big. fork() of a large process usually succeeds (COW).
  • 1 — always overcommit. Needed for e.g. Redis's fork-to-save; risk of the OOM killer firing later.
  • 2 — strict: never overcommit beyond swap + overcommit_ratio% of RAM. malloc fails early instead of an OOM kill later — cleaner for some workloads, but breaks apps that allocate big and use little.

vm.panic_on_oom=1 makes the box panic (and reboot, with a watchdog) instead of killing — sometimes preferable for a cluster node.

Prevention

  • Cap the memory hog in its own cgroup so it's OOM-killed inside its cgroup, not taking the host down: MemoryMax=2G on the unit (see cgroups v2 basics).
  • Right-size or add swap. A few GB of swap turns a hard OOM into slow reclaim (and gives you time to react). zram swap is a good middle ground.
  • Fix the leak. smem, ps_mem, /proc/<pid>/smaps_rollup, or a heap profiler.
  • Set MemoryHigh (soft) below MemoryMax — the kernel throttles and reclaims aggressively before the hard kill.
  • earlyoom / systemd-oomd — userspace daemons that act earlier and more gracefully (SIGTERM first, pick by cgroup pressure) than the kernel killer.

Verification and troubleshooting

free -h; swapon --show
grep -i oom /var/log/syslog /var/log/messages
systemctl status myapp   # "Main process exited, code=killed, status=9/KILL" after an OOM
cat /sys/fs/cgroup/system.slice/myapp.service/memory.events   # oom, oom_kill counters
  • App "randomly" dies with exit 137 / SIGKILL, no app-level error — OOM kill. Check dmesg. 137 = 128 + 9.
  • OOM killed the wrong process — the victim had the biggest RSS at that instant. Protect it with OOMScoreAdjust and cap the actual hog.
  • No OOM message but processes still dying — a cgroup OOM (check memory.events), or the app is catching a different signal, or it's systemd-oomd (check journalctl -u systemd-oomd).
  • vm.overcommit_memory=2 broke an app on startup — it allocated a large arena upfront. Either that app needs 0/1, or tune overcommit_ratio.
  • Box hangs before OOM fires — thrashing on a tiny swap; the killer is slow to trigger under heavy reclaim. earlyoom/systemd-oomd help; or less swap so it OOMs faster; or (best) more RAM / a memory limit.
  • Container OOMKilled — the container's memory limit, not the host. Raise the limit or fix the workload; the runtime reports OOMKilled separately from a host OOM.
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.