<aside> ๐Ÿงญ

Module 09 ยท Page cache, swap and the OOM killer

This is the module that fixes the single most common misunderstanding in Linux operations: "the server has no free memory". It almost always does, and the number people are reading is the wrong one. By the end you will be able to say exactly how much memory a machine really has left, and what happens on the day it genuinely runs out.

๐Ÿง  concept โ†’ ๐Ÿงฉ real-world analogy โ†’ ๐Ÿงช exercise โ†’ โœ… expected result (hidden) โ†’ ๐ŸŽฏ interview questions (hidden)

</aside>

<aside> โœ…

Before you start, you should already know:

From Module 03 โ€” that a file's contents live in blocks on a disk, and what an inode is.

From Module 07 โ€” how to read /proc/pressure/, and that cgroups can cap a workload's resources. Section C3 goes further into cgroup v2's memory files; everything it needs is explained there, and Module 12 is where namespaces and containers are covered properly.

From Module 08 โ€” pages and frames, minor versus major faults, file-backed versus anonymous memory, and the meaning of Private_Dirty. This module depends on all of it.

</aside>


๐Ÿ“š Part A ยท The page cache

A1 ยท Why free memory is wasted memory

<aside> ๐Ÿ“–

Official docs: Memory management โ€” concepts overview ยท proc_meminfo(5) ยท Linux ate my RAM

</aside>

Disk is roughly a hundred thousand times slower than RAM. So when Linux reads a file from disk, it does something obvious in hindsight: it keeps the copy. The next process that wants those bytes gets them from memory instead of from the disk.

That store of file contents held in RAM is the page cache. It is not a small buffer. On a healthy, long-running machine it will grow until it has consumed essentially all otherwise-unused memory, because there is no reason to leave RAM empty.

The consequence is the thing that confuses everybody:

<aside> ๐Ÿง 

A well-run Linux server shows almost no free memory, and that is the correct state. Free memory is memory doing nothing. The kernel would rather fill it with cached file data โ€” which it can throw away in microseconds the moment a program actually needs the space. Low free is not a warning sign. It is the system working.

</aside>

The page cache is also not lost memory. Clean cached pages โ€” ones that match what is on disk โ€” can be dropped instantly, with no writing and no waiting, because the data is still on the disk. That is why a machine showing 200 MB free can start a program needing 3 GB without any difficulty.

<aside> ๐Ÿงฉ

Real-world analogy โ€” the librarian's desk

A librarian works at a desk in front of a very large, very slow archive in the basement. Fetching a volume from the archive takes twenty minutes.

So when a book comes up, she does not send it back down after use. She leaves it on the desk. The desk fills up with books nobody has explicitly asked for again โ€” and a visitor glancing in would say "there is no free space on that desk, she must be overwhelmed".

The opposite is true. An empty desk would mean every single request costs a twenty-minute trip. The full desk is the whole point of having a desk.

And the desk is not really full, because the books on it are copies of things still safely in the archive. When someone needs to spread out a large map, she sweeps a stack of them aside instantly โ€” no need to carry anything back down, because nothing on the desk is unique.

The one exception is a book she has been annotating. That one is not a copy any more. Before that space can be reused, the annotations must be carried back down and filed. Those are dirty pages, and Section A3 is about them.

Where the analogy stops working. A librarian chooses what to keep by judgement. The kernel uses a mechanical rule based on recency and reuse, with no idea what any of the data means.

</aside>

๐Ÿงช Exercise A1.1 โ€” Watch the cache fill, and watch it pay off

# A file big enough to notice, but small enough to be quick
dd if=/dev/urandom of=/tmp/cachetest bs=1M count=512 status=none

# Start from a cold cache: forget everything held from disk
sync; sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'

# How much file data is the kernel holding right now?
grep -E '^(MemFree|Buffers|Cached):' /proc/meminfo

# First read: this has to come off the disk
echo "--- cold read ---"
time cat /tmp/cachetest > /dev/null

grep -E '^(MemFree|Buffers|Cached):' /proc/meminfo

# Second read: identical command, identical file
echo "--- warm read ---"
time cat /tmp/cachetest > /dev/null

rm -f /tmp/cachetest

<aside> ๐Ÿญ

Now imagine this at 500 hosts. "Memory usage above 90%" is the most common bad alert in existence. It fires constantly on healthy machines whose page cache has done its job, so teams either raise the threshold until it is useless or learn to ignore it โ€” and then miss the one host that is genuinely in trouble. The correct signal is built from MemAvailable (Section A2) or, better, from memory pressure (Section B3). Alerting on MemFree is alerting on how much of your RAM is being wasted.

</aside>

A2 ยท Reading free and /proc/meminfo properly

<aside> ๐Ÿ“–

Official docs: free(1) ยท proc_meminfo(5) ยท The /proc filesystem

</aside>