Memory Overcommit And OOM Killer
What happens when an embedded board truly runs out of RAM — free linux kernel development course
Every earlier lecture in this free linux kernel development course assumed memory was available to allocate. This lecture covers what happens when it genuinely is not: the kernel’s overcommit policy, the tuning knobs that control it, and the OOM killer — the last line of defense when demand for memory outruns supply.
Keywords covered
What You Will Learn
- Why the kernel allows applications to allocate more memory than physically exists
- The three overcommit policies and when each makes sense on an embedded board
- How to read CommitLimit and Committed_AS from /proc/meminfo
- How the OOM killer chooses a victim, and how to influence that choice
- An original demo that safely triggers and observes overcommit limits
Prerequisites
Root access on a test board or VM you are comfortable rebooting — some of the commands here deliberately push the system toward its memory limits, and should never be run on production hardware.
Why The Kernel Overcommits Memory By Default
The standard Linux memory allocation policy is to over-commit: the kernel lets applications reserve more memory than physically exists. This sounds reckless, but it reflects how real programs behave — most applications ask for more address space than they ever actually touch, and reserving without immediately backing every page keeps allocation fast and cheap. It is also essential for fork(2): forking a large process only needs to share its pages with copy-on-write semantics, not duplicate them in RAM — and since fork is almost always followed by exec, which discards the parent’s memory entirely, most of that “reserved” memory is never even copied.
The Three Overcommit Policies
The policy is controlled by /proc/sys/vm/overcommit_memory:
| Value | Policy | Best for |
|---|---|---|
| 0 (default) | Heuristic over-commit | General-purpose systems; reasonable default |
| 1 | Always over-commit, never check | Programs using large sparse arrays, rare on embedded targets |
| 2 | Always check, never over-commit | Mission- or safety-critical embedded systems that must fail predictably |
With policy 2, allocations that would exceed the commit limit fail outright instead of succeeding and risking an OOM event later. The commit limit is swap space plus total RAM, multiplied by the overcommit ratio (/proc/sys/vm/overcommit_ratio, default 50%).
Checking Your Board’s Headroom
Two fields in /proc/meminfo tell you exactly where you stand — the enforced ceiling, and how much has already been promised:
# echo 25 > /proc/sys/vm/overcommit_ratio
# grep -e MemTotal -e CommitLimit /proc/meminfo
MemTotal: 509016 kB
CommitLimit: 127252 kB
# grep -e MemTotal -e Committed_AS /proc/meminfo
MemTotal: 509016 kB
Committed_AS: 741364 kB
Here Committed_AS (741 MB promised) already exceeds CommitLimit (127 MB allowed) — under policy 2 this board’s remaining allocations would all fail, regardless of how much RAM is physically free at that instant, until either RAM is added or the number of running processes is reduced.
The OOM Killer
Even with the default heuristic policy, a workload can eventually promise more memory than the system can deliver. At that point there is no polite way out — some process must be killed to free memory. The kernel’s OOM killer computes a heuristic badness score from 0 to 1000 for every process, and terminates the process (or processes) with the highest score until enough memory is free again. A kill shows up in the kernel log like this:
[44510.490320] ep_hogger invoked oom-killer: gfp_mask=0x200da, order=0, oom_score_adj=0
...
Out of memory: Killed process 2113 (ep_hogger) total-vm:524288kB, anon-rss:498212kB
You can influence — or exempt — a specific process from selection by writing an adjustment to /proc/<PID>/oom_score_adj: a value of -1000 means the badness score can never exceed zero, so that process will never be chosen; +1000 guarantees it will always be the top candidate.
Original Demo: Observing Overcommit Safely
This demo, ep_hogger, grows its own memory usage in controlled steps and prints its PID so you can watch /proc/<PID>/oom_score_adj and system memory together — run it only on a disposable VM, never a production board:
/* ep_hogger.c - controlled memory growth for observing overcommit */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define STEP_MB 32
int main(int argc, char *argv[])
{
int steps = (argc > 1) ? atoi(argv[1]) : 8;
printf("ep_hogger pid=%d - growing %d MB per step, %d steps\n",
getpid(), STEP_MB, steps);
for (int i = 0; i < steps; i++) {
char *block = malloc(STEP_MB * 1024 * 1024);
if (!block) {
printf("step %d: allocation FAILED (overcommit limit hit)\n", i);
return 1;
}
memset(block, 0x33, STEP_MB * 1024 * 1024); /* force residency */
printf("step %d: now holding roughly %d MB\n", i, (i + 1) * STEP_MB);
sleep(2);
}
printf("ep_hogger finished without hitting a limit\n");
return 0;
}
Run it under the conservative policy from the earlier example, in a second terminal watch Committed_AS climb:
$ echo 2 > /proc/sys/vm/overcommit_memory
$ echo 25 > /proc/sys/vm/overcommit_ratio
$ ./ep_hogger 20
ep_hogger pid=2551 - growing 32 MB per step, 20 steps
step 0: now holding roughly 32 MB
step 1: now holding roughly 64 MB
...
step 4: allocation FAILED (overcommit limit hit)
Under policy 2 the allocator fails cleanly with ENOMEM once the commit limit is reached — no kill, no crash, exactly the predictable behavior mission-critical embedded software wants. Contrast that with the default heuristic policy (overcommit_memory=0), where the same program is more likely to be allowed to keep allocating until the OOM killer eventually steps in and kills it (or another process) outright.
You can also force an OOM event directly for testing, on a disposable system only:
# echo f > /proc/sysrq-trigger
Overcommit Policy Comparison
| Policy | Failure mode | Predictability |
|---|---|---|
| 0 (heuristic) | OOM killer picks a victim later, possibly not the offending process | Low — kill can hit an unrelated process |
| 2 (never overcommit) | Allocation itself fails immediately with ENOMEM | High — the requesting process gets a clear, handleable error |
Common Mistakes
- Assuming free RAM equals allocatable RAM under policy 2 —
Committed_AScan already exceedCommitLimiteven while RAM shows as “free” at that instant. - Setting
oom_score_adj=-1000on everything “important” — if too many processes are exempted, the killer may be forced to pick from an unsuitable remaining pool, or fail to free enough memory at all. - Testing overcommit policy 2 changes on production hardware — a too-low overcommit ratio can make legitimate allocations fail unexpectedly across the whole system.
Best Practices
- On safety- or mission-critical boards, prefer
overcommit_memory=2with a ratio sized from real measuredCommitted_ASheadroom, not a guess. - Set
oom_score_adjdeliberately on your most critical watchdog or supervisor process so it survives an OOM event that kills something less important. - Monitor
Committed_ASalongside free memory in your on-device logging — it warns you before the OOM killer has to act.
Security Considerations
An unprivileged process that can allocate unbounded memory can effectively deny service to the rest of the board by triggering the OOM killer against unrelated processes. Combine per-process memory limits (cgroups) with a sane overcommit ratio to bound worst-case impact.
Summary And Key Takeaways
Linux overcommits memory by default because most reserved memory is never actually touched, and this keeps common operations like fork() cheap. When commitments genuinely exceed capacity, either the allocator fails predictably (policy 2) or the OOM killer intervenes reactively (policy 0), using a badness score you can influence via oom_score_adj.
Conclusion
Understanding overcommit and the OOM killer closes the loop on memory management for embedded Linux: from measuring usage per process, through catching leaks, to handling the day genuine physical memory runs out. That progression is the backbone of the memory management unit in this free linux kernel development course.
FAQ
What does overcommit_memory=0 do?
It enables the kernel’s default heuristic over-commit policy, allowing most allocations to succeed while relying on the OOM killer if memory genuinely runs out.
What is the safest overcommit setting for safety-critical embedded systems?
overcommit_memory=2, which makes allocations fail predictably with ENOMEM instead of risking a later OOM kill.
How is the commit limit calculated?
CommitLimit equals swap space plus total RAM, multiplied by the overcommit_ratio (default 50%).
How does the OOM killer choose which process to kill?
It computes a heuristic badness score from 0 to 1000 for each process and kills the process with the highest score first.
How can I protect a critical process from the OOM killer?
Write -1000 to /proc/PID/oom_score_adj so its badness score can never exceed zero, exempting it from selection.
Can I force an OOM event for testing?
Yes, echo f to /proc/sysrq-trigger on a disposable test system, never on production hardware.
Does Committed_AS ever exceed CommitLimit under normal operation?
Yes, and once it does under overcommit_memory=2, all further allocations fail until memory is freed or the ratio is raised.
Continue The Free Linux Kernel Development Course
That completes the memory management unit — next up, debugging with GDB.
Next Lecture Course Index
2 Comments