Skip to main content
RunBook Academy

LinuxXL · Memory PerformancePage cache anon

Page cache and anonymous memory - the two memory kinds

Intermediate⏱ ~10 minfreevmtouch

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

Not yet marked complete on this device.

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 (Dirty in /proc/meminfo).
  • Writeback: pages actively being written (Writeback).
grep -E 'Cached|Buffers|Dirty|Writeback' /proc/meminfo

Reading:

  • Cached is large: many files cached. Performance benefit.
  • Dirty is high: writes are pending. Disk write pressure.
  • Writeback is 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:

  1. Clean page cache: drop pages that are not dirty. Free.
  2. Dirty page cache: write to disk, then drop.
  3. Swap anonymous memory: write to swap, free the page.
  4. 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

PatternMeaning
High Cached, low SwapHealthy: cache is reclaimable
High AnonPages, low CachedMemory pressure; swap imminent
High DirtyDisk write pressure
High Inactive(anon)Reclaimable anonymous memory
si/so > 0 in vmstatSwapping is happening

Knowledge check

Knowledge check · 4 questions

  1. Q1. How is the page cache reclaimed?

  2. Q2. Anonymous memory can be discarded by the kernel like page cache.

  3. Q3. Which of the following are valid kernel VM tuning parameters? Select all that apply.

  4. 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.