Linux Memory: Anonymous, File-backed, zswap
Deep dive into Linux anonymous vs file-backed pages, reclaim mechanics, vm.swappiness, zswap compression tuning, and zram setup for maximum memory efficiency.
Before you start
- ▸Root or sudo access on the target system
- ▸Kernel 5.8 or later recommended (vm.swappiness 0-200 range, modern zswap defaults)
- ▸Basic familiarity with sysctl and reading /proc/meminfo
- ▸sysstat package installed for sar/vmstat extended output
Linux memory management is subtle. The kernel tracks every page of RAM and decides, under pressure, what to keep hot and what to evict or compress. Getting that decision wrong means either a sluggish desktop or a thrashing server. This guide walks through the two fundamental page types, how reclaim works, and how to tune vm.swappiness, zswap, and zram for real workloads.
Anonymous vs File-Backed Pages
Every page in RAM belongs to one of two classes:
- File-backed pages are backed by a file on disk — executable code, shared libraries, memory-mapped data files, page cache for reads and writes. If the kernel needs the RAM, it can simply drop clean file pages (they can be re-read from disk on demand) or write dirty ones back before dropping them.
- Anonymous pages have no file behind them. They are heap allocations (
malloc), stack frames, and privatemmapregions. The only place they can go when evicted is swap space.
You can inspect the split at any moment:
grep -E 'Active|Inactive|Anon|File|Swap' /proc/meminfo
The output will show Active(anon), Inactive(anon), Active(file), and Inactive(file) — the kernel's own LRU lists. Pages age from active to inactive before they become candidates for reclaim.
How Reclaim Works
The kernel runs two reclaim paths:
- kswapd — a per-NUMA-node background thread that wakes up when free memory falls below
pages_lowwatermark. It reclaims pages gradually to keep a small buffer of free RAM. - Direct reclaim — triggered synchronously when an allocation cannot be satisfied from free lists and kswapd is not keeping up. This stalls the allocating process and is expensive.
Watch whether you are hitting direct reclaim with:
vmstat 2 5
The si and so columns show swap-in and swap-out rates per second. Sustained non-zero values mean the system is reclaiming anonymous memory under pressure.
For a richer view, sar from sysstat shows page fault rates and reclaim activity over time:
sar -B 1 5
Tuning vm.swappiness
vm.swappiness controls the kernel's tendency to reclaim anonymous pages (pushing them to swap) versus reclaiming file-backed pages (dropping them from the page cache). It is not a simple swap-on/swap-off switch.
- Range: 0–200 (kernels ≥ 5.8 extended it to 200; older kernels cap at 100).
- Default: 60 on most distributions.
- Higher value → prefer reclaiming anonymous pages (swap more aggressively).
- Lower value → prefer reclaiming file cache; anonymous pages stay in RAM longer.
- Value 0 does not disable swap; it just makes the kernel very reluctant to reclaim anonymous pages until absolutely necessary.
Check and Set Temporarily
cat /proc/sys/vm/swappiness
sysctl vm.swappiness=10
Make it Persistent
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-memory.conf
sudo sysctl --system
Practical guidance: desktops with 16 GB+ RAM typically benefit from a value of 10–20, keeping interactive applications' heap in RAM. Database servers (PostgreSQL, MySQL) often do well at 10 or even 1, since their buffer pools are anonymous memory and eviction is catastrophic for latency. Servers dominated by file I/O can tolerate higher values.
zswap: Compressed Swap Cache
zswap is a kernel subsystem that intercepts pages on their way to swap, compresses them in a small RAM pool, and only writes to actual swap if that pool fills up. The result is faster swap-out/swap-in (compression in RAM beats a disk round-trip) at the cost of some CPU and a reserved chunk of RAM.
Check Whether zswap Is Active
cat /sys/module/zswap/parameters/enabled
grep -r . /sys/kernel/debug/zswap/ 2>/dev/null
Many modern distributions (Ubuntu 22.04+, Fedora 34+) enable zswap by default. If the first command returns N, you can enable it at runtime:
echo 1 | sudo tee /sys/module/zswap/parameters/enabled
For a persistent boot-time configuration, add kernel parameters. On systemd-boot or GRUB:
# Add to GRUB_CMDLINE_LINUX in /etc/default/grub:
# zswap.enabled=1 zswap.compressor=lz4 zswap.max_pool_percent=20
# Debian/Ubuntu — rebuild grub after editing:
sudo update-grub
# Fedora/RHEL:
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# Arch (systemd-boot — edit the loader entry directly):
sudo nano /boot/loader/entries/*.conf
# append to the options line: zswap.enabled=1 zswap.compressor=lz4 zswap.max_pool_percent=20
Key zswap Parameters
| Parameter | Default | Notes |
|---|---|---|
compressor | lzo-rle or zstd | lz4 is fastest; zstd gives better ratio. Check cat /sys/module/zswap/parameters/compressor. |
max_pool_percent | 20 | Percentage of total RAM the zswap pool can use. 10–20 % is typical. |
zpool | zsmalloc | Allocator for compressed pages. zbud uses less CPU; zsmalloc packs more densely. |
Tune compressor at runtime (no reboot needed if the module is loaded):
echo lz4 | sudo tee /sys/module/zswap/parameters/compressor
echo 15 | sudo tee /sys/module/zswap/parameters/max_pool_percent
zram: Compressed RAM Swap
zram creates a block device backed entirely by RAM, formatted as swap. Unlike zswap (which is a cache in front of disk swap), zram is the swap. There is no disk fallback unless you also configure a swap partition. It is the default on Android, ChromeOS, and is now the default on Fedora, Ubuntu 22.10+, and Arch's systemd-zram-generator.
Check Current zram Status
zramctl
swapon --show
Configure with systemd-zram-generator (Recommended)
Install the generator if not present:
# Fedora/RHEL:
sudo dnf install zram-generator
# Debian/Ubuntu:
sudo apt install systemd-zram-generator
# Arch:
sudo pacman -S zram-generator
Create or edit the configuration:
sudo nano /etc/systemd/zram-generator.conf
A typical configuration:
[zram0]
# Size: half of physical RAM, up to 8 GB
zram-size = min(ram / 2, 8192)
compression-algorithm = zstd
swap-priority = 100
Apply without rebooting:
sudo systemctl daemon-reload
sudo systemctl start /dev/zram0
swapon --show
Manual zram Setup (No Generator)
sudo modprobe zram
echo lz4 | sudo tee /sys/block/zram0/comp_algorithm
# Set size to 4 GB:
echo $((4 * 1024 * 1024 * 1024)) | sudo tee /sys/block/zram0/disksize
sudo mkswap /dev/zram0
sudo swapon -p 100 /dev/zram0
Note: zswap and zram together make little sense — you would be double-compressing. Disable zswap if you are using zram as your only swap backend:
echo 0 | sudo tee /sys/module/zswap/parameters/enabled
Verification
# Overall memory and swap picture:
free -h
# Confirm swap type and priority:
swapon --show
# Live compression stats for zram:
zramctl
# zswap hit/miss rates (if using zswap):
grep -r . /sys/kernel/debug/zswap/
# Current sysctl values:
sysctl vm.swappiness vm.vfs_cache_pressure
Troubleshooting
zswap Not Appearing in /sys/kernel/debug/zswap/
The debug FS may not be mounted. Run sudo mount -t debugfs none /sys/kernel/debug and retry. Add it to /etc/fstab if needed.
zram Device Busy on Resize
zram parameters cannot be changed while the device is active. Run sudo swapoff /dev/zram0, make changes, then re-enable. With the generator, edit the config and run systemctl restart [email protected].
High CPU Usage After Enabling Compression
Switch from zstd to lz4 for lower latency at the cost of compression ratio. On modern CPUs with many idle cores, zstd is usually fine; on embedded or single-core systems, lz4 or lzo is preferable.
Swap Still Going to Disk Despite zswap
zswap fills its pool and spills to disk swap when the pool hits max_pool_percent. Increase the pool or reduce total memory pressure. Check zswap.pool_total_size vs zswap.stored_pages in debugfs to see how full the pool is.
Frequently asked questions
- Does setting vm.swappiness=0 disable swap entirely?
- No. A value of 0 makes the kernel extremely reluctant to reclaim anonymous pages but it will still swap under genuine memory pressure. To truly disable swap, run swapoff -a.
- Can I run both zswap and zram at the same time?
- Technically yes, but it is wasteful. zswap would compress pages before sending them to the zram device, which compresses them again. Choose one: zswap if you have a disk swap backing store, zram if you want a pure in-RAM compressed swap target.
- How much RAM should I allocate to zram?
- A common starting point is half of physical RAM with a ceiling of 8 GB, as expressed by min(ram/2, 8192) in the generator config. With a 3:1 average compression ratio, this effectively provides up to 24 GB of swap space from 8 GB of RAM.
- Which compression algorithm should I pick for zswap or zram?
- lz4 has the lowest CPU overhead and is best for latency-sensitive workloads or slow CPUs. zstd achieves significantly higher compression ratios (useful when RAM is severely limited) at moderate CPU cost. lzo-rle is a reasonable middle ground on older kernels.
- Why does the kernel still evict file cache even with low swappiness?
- vm.swappiness biases the balance but doesn't prevent file-cache eviction. Under heavy memory pressure the kernel reclaims whatever is cheapest. Dirty file pages must be written back before eviction, so clean file cache often gets dropped first regardless of swappiness.
Related guides
AI and Artificial-Life Tools on Linux
Set up open-source AI/ML and artificial-life toolkits on Linux: PyTorch, JAX, DEAP, Avida, NetLogo, and RL environments with GPU driver guidance.
Assembly Language on Linux: A Starter Guide
Write x86-64 assembly on Linux from scratch: install NASM and GAS, learn syscalls, assemble and link a working program, then inspect and debug it.
How to Benchmark Disk Performance with fio
Learn to benchmark Linux disk performance with fio: writing job files, testing latency and throughput, and interpreting IOPS and percentile output correctly.
The Linux Boot Process Explained
Trace the full Linux boot sequence from UEFI firmware through GRUB2, the kernel, initramfs, and systemd to your login prompt — with diagnostics at each stage.