LinuxXL · Memory PerformancePage cache anon
Page cache and anonymous memory - the two memory kinds
What you'll learn
- Distinguish page cache from anonymous memory
- Explain how each is reclaimed
- Tune the page cache
- Recognise when swap is needed
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
Linux memory has two fundamentally different kinds: page cache (file-backed) and anonymous memory (process heap/ stack). The kernel reclaims each differently.
Page cache
The page cache is the kernel’s file cache. When a process reads a file, the pages are kept in memory. Subsequent reads are served from RAM.
- Discardable: when memory is needed, the kernel can throw away page cache (the data is on disk).
- Dirty: pages modified but not written to disk yet
(
Dirtyin /proc/meminfo). - Writeback: pages actively being written (
Writeback).
grep -E 'Cached|Buffers|Dirty|Writeback' /proc/meminfo
Reading:
Cachedis large: many files cached. Performance benefit.Dirtyis high: writes are pending. Disk write pressure.Writebackis high: disk is being written to.
Anonymous memory
Anonymous memory has no file backing. It must be written to swap or compressed to be reclaimed.
- Heap, stack, BSS: typical anonymous memory.
- mmap with MAP_ANONYMOUS: also anonymous.
- Reclaim: write to swap (or zswap), free the page.
grep -E 'AnonPages|Active\(anon\)|Inactive\(anon\)' /proc/meminfo
Reclaim
The kernel reclaims memory in this order:
- Clean page cache: drop pages that are not dirty. Free.
- Dirty page cache: write to disk, then drop.
- Swap anonymous memory: write to swap, free the page.
- OOM kill: if all else fails, kill a process.
zswap (compressed swap in RAM) can absorb step 3 without
touching the disk, but it is compiled in and off by
default on Debian, Ubuntu and RHEL. Check
/sys/module/zswap/parameters/enabled before assuming a
host has it.
Tune the page cache
The kernel manages the page cache automatically. For specific tuning:
# /proc/sys/vm/dirty_ratio
# Percentage of AVAILABLE memory (free + reclaimable) that may
# be dirty before a writing process is blocked and forced to
# do writeback itself
# Default: 20
# /proc/sys/vm/dirty_background_ratio
# Percentage at which kernel writeback threads start flushing
# in the background, without blocking anyone
# Default: 10
# /proc/sys/vm/vfs_cache_pressure
# Tendency to reclaim dentry and inode caches (not the page
# cache itself)
# Default: 100
# Lower = keep dentries/inodes longer
# Higher = reclaim them more aggressively
For write-heavy and database workloads, flush early and continuously so writeback never accumulates into a burst. Prefer the byte-valued knobs, so the threshold does not silently scale with the size of the machine:
# Start background writeback at 256 MB of dirty data
sudo sysctl -w vm.dirty_background_bytes=268435456
# Hard-throttle writers at 1 GB
sudo sysctl -w vm.dirty_bytes=1073741824
Setting the _bytes knobs zeroes the corresponding _ratio
knobs, and vice versa - they are two views of one setting, and
only one is active at a time. Verify which one is live:
sysctl vm.dirty_ratio vm.dirty_bytes \
vm.dirty_background_ratio vm.dirty_background_bytes
Size the values against your storage, not against your RAM.
The useful question is “how much data can this device absorb
in a second or two?”, because that is how long a flush should
take. A 1 GB dirty_bytes on an NVMe device that sustains
1 GB/s is roughly a one-second flush. The same 1 GB on a
spinning disk that sustains 150 MB/s is a seven-second stall.
Leave vfs_cache_pressure at its default of 100 unless you
have evidence. It governs the dentry and inode caches, not the
page cache. Raising it to 1000 on a database host is
counterproductive - it throws away exactly the metadata the
database needs to reopen its files. Lowering it below 100 is
occasionally useful on hosts with very large directory trees,
such as mail and file servers.
zswap
zswap is a compressed cache for swap pages. Instead of writing to disk (slow), pages are compressed in RAM. If the cache fills, oldest pages are evicted (and written to disk if swap is configured).
zswap is a module parameter, not a sysctl. There is no
vm.zswap_enabled; the knobs live under /sys/module/zswap/:
# Is it on? "N" on a stock Debian, Ubuntu or RHEL host
cat /sys/module/zswap/parameters/enabled
# Enable at runtime
echo 1 | sudo tee /sys/module/zswap/parameters/enabled
echo zstd | sudo tee /sys/module/zswap/parameters/compressor
echo 20 | sudo tee /sys/module/zswap/parameters/max_pool_percent
A runtime write does not survive a reboot, and enabling zswap after the host is already swapping does nothing for the pages already on disk. Persist it on the kernel command line so it is active from boot:
zswap.enabled=1 zswap.compressor=zstd zswap.max_pool_percent=20
zswap is good for workloads with bursty memory usage and fast disk.
Common patterns
| Pattern | Meaning |
|---|---|
High Cached, low Swap | Healthy: cache is reclaimable |
High AnonPages, low Cached | Memory pressure; swap imminent |
High Dirty | Disk write pressure |
High Inactive(anon) | Reclaimable anonymous memory |
si/so > 0 in vmstat | Swapping is happening |
Knowledge check
Knowledge check · 4 questions
Q1. How is the page cache reclaimed?
Q2. Anonymous memory can be discarded by the kernel like page cache.
Q3. Which of the following are valid kernel VM tuning parameters? Select all that apply.
Q4. A 128 GB PostgreSQL host stalls for 20-30 seconds every few minutes. During the stall, Dirty in /proc/meminfo drops from tens of gigabytes to near zero and disk utilisation is pinned at 100%. Someone suggests raising vm.dirty_ratio so the host can absorb more writes. What should you do?
Passing score: 75%. Answers are checked in this browser.