---
title: 'Swap Space'
source: 'https://academia.sh/en/courses/system-administration/swap-space'
course: 'System Administration'
language: en
updated: '2026-08-17T18:10:06+00:00'
license: 'CC BY-SA 4.0'
---

# Swap Space

Pressure lasts 84 seconds, but the used-swap indicator raises a false alarm for 272 seconds, free memory for 451; as the swap area grows, the closability diagnosis rises from 44 wrong to 354 wrong.

The previous lessons treated storage only as a layer that holds data:
partitions, file systems, mount points, and logical volumes. A portion of
the same block device, though, holds no files at all and appears at no
mount point. That portion is where pages are written when memory falls
short.

This lesson's question is not one of storage but of **seeing**: in which
indicator does swap usage show up, and how late. The answer will not come
out one-directional. Some indicators report pressure late, one keeps
reporting it even after the pressure has passed, and one shouts even when
there is no pressure at all.

## Swap Space and What It Is Not

**Swap** is the disk space pages that do not fit in physical memory get
written to. It can be a partition, it can be a file; both are formatted the
same way and activated the same way. The file form is flexible because it
can be added and removed afterward, the partition form is plain because it
is independent of the file system's state. The difference between them is
not a ranking of superiority but a management preference.

The mechanics of paged memory were measured in the Operating System
Concepts course: translating a virtual page to a physical page, the page
fault, and the page-replacement procedure are defined there and are not
repeated here. This lesson counts only what the operator can see.

What can be seen is a handful of numbers: swap's total size, its used
portion, pages written to swap and read from swap per second, free memory,
and available memory. These are read through `swapon --show`, `free`, and
the file holding memory statistics. The transcript below is a **sample
transcript** showing the shape of the output; it was not run, and no
numeric claim in this lesson comes from it.

```text
NAME      TYPE       SIZE   USED PRIO
/dev/sdb4 partition  4G     1.2G   -2

               total        used        free      shared  buff/cache   available
Mem:            7.6G        4.1G        160M         88M        3.3G        3.1G
Swap:           4.0G        1.2G        2.8G
```

The `free` and `available` columns being separate in the transcript is the
subject of this lesson's first measurement: one does not count reclaimable
cache as free, the other does.

## Four Indicators, Four Separate Errors

The measurement is done in a 600-second window on the fictional server. The
report-producing unit starts growing gradually from the two-hundredth
second, finishes its work at the three-hundred-twentieth second; there is
one more short, sharp spike at the four-hundred-eightieth second.

- **ST32** — Physical memory is **1200 pages**, the usual resident demand
  is **900 pages**. Pressure is the second demand exceeds physical memory,
  and the oracle knows this.
- **ST33** — A page written to swap stays there; used swap does **not
  decrease** until it is read back. This is the behavior that is the axis
  of the measurement.
- **ST34** — The cache fills free memory and grows by **four pages** per
  second. Available memory counts reclaimable cache as free.
- **ST35** — The warning threshold is **100 pages**. The tool looks once
  every `interval` seconds; between two looks, the operator's belief is
  the last reading.

```python
"""How late (or how early) swap usage shows up in each indicator."""
SEED = 20260218
PERIOD = 600
PHYSICAL = 1200          # physical memory, pages
BASELINE = 900           # usual resident demand
THRESHOLD = 100          # memory warning threshold, pages
AMPLITUDE = 60            # fluctuation in demand


def generator(seed):
    d = seed

    def next_value(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return next_value


def demand(seed=SEED):
    """Oracle: the resident pages really requested every second."""
    r = generator(seed)
    values = []
    for t in range(PERIOD):
        if 200 <= t < 320:
            base = BASELINE + (t - 200) * 6      # report job grows gradually
        elif 480 <= t < 495:
            base = 1700                           # short, sharp spike
        else:
            base = BASELINE
        values.append(base + r(2 * AMPLITUDE + 1) - AMPLITUDE)
    return values


def curves(seed=SEED):
    d_list = demand(seed)
    used, rate, free, available, pressure = [], [], [], [], []
    peak = previous = 0
    for t, d in enumerate(d_list):
        overflow = max(0, d - PHYSICAL)
        delta = abs(overflow - previous)          # pages sent to and read from swap per second
        previous = overflow
        peak = max(peak, overflow)                # written page stays until read back
        headroom = PHYSICAL - min(d, PHYSICAL)
        cache = min(4 * t, headroom)               # cache fills the free space
        used.append(peak)
        rate.append(delta)
        free.append(headroom - cache)
        available.append(headroom)                # reclaimable cache is not counted
        pressure.append(d > PHYSICAL)
    return {"used": used, "rate": rate, "free": free,
            "available": available, "pressure": pressure}


RULE = {"used swap": lambda e, i: e["used"][i] > 0,
        "swap rate": lambda e, i: e["rate"][i] > 0,
        "free memory": lambda e, i: e["free"][i] < THRESHOLD,
        "available memory": lambda e, i: e["available"][i] < THRESHOLD}


def count(e, rule, interval):
    missed = false_alarm = 0
    last = False
    for t in range(PERIOD):
        if t % interval == 0:
            last = rule(e, t)
        missed += e["pressure"][t] and not last
        false_alarm += last and not e["pressure"][t]
    return missed, false_alarm


E = curves()
print("oracle: seconds under pressure:", sum(E["pressure"]), "/", PERIOD)
print("indicator        t=100  t=240  t=300  t=330  t=485  t=560")
for name in ("used", "rate", "free", "available"):
    print(f"  {name:14s}", "  ".join(f"{E[name][t]:5d}" for t in
                                      (100, 240, 300, 330, 485, 560)))
print()
print("indicator              interval  missed  false alarm  wrong diagnosis")
for name, rule in RULE.items():
    for interval in (5, 30, 60, 120):
        missed, false_alarm = count(E, rule, interval)
        print(f"  {name:21s} {interval:6d}  {missed:9d}  {false_alarm:12d}"
              f"  {missed + false_alarm:11d}")
print()
for seed in (SEED, 20260219):
    e = curves(seed)
    s = " | ".join(f"{name.split()[0]} {sum(count(e, k, 5))}"
                    for name, k in RULE.items())
    print(f"seed {seed}: pressure {sum(e['pressure']):3d} seconds | interval 5 : {s}")
```

```
oracle: seconds under pressure: 84 / 600
indicator        t=100  t=240  t=300  t=330  t=485  t=560
  used               0      0    331    423    533    535
  rate               0      0     57      0     61      0
  free               0      0      0      0      0      0
  available        268    120      0    258      0    333

indicator              interval  missed  false alarm  wrong diagnosis
  used swap                  5          1           272          273
  used swap                 30         19           265          284
  used swap                 60         49           265          314
  used swap                120         69           225          294
  swap rate                  5          2            13           15
  swap rate                 30         19            25           44
  swap rate                 60         49            85          134
  swap rate                120         69           105          174
  free memory                5          0           451          451
  free memory               30          0           426          426
  free memory               60          0           396          396
  free memory              120          0           396          396
  available memory           5          1            12           13
  available memory          30         19            25           44
  available memory          60         49            85          134
  available memory         120         69           105          174

seed 20260218: pressure  84 seconds | interval 5 : used 273 | swap 15 | free 451 | available 13
seed 20260219: pressure  86 seconds | interval 5 : used 271 | swap 16 | free 469 | available 9
```

Three numbers side by side. **The oracle:** the window has **84 seconds**
of real memory pressure. **The tool's output:** the indicator whose name
directly names swap, used swap, sits at **423** at the
three-hundred-thirtieth second, at **533** after the four-hundred-eighty-fifth,
and never comes down. **Wrong diagnosis:** even read once every five
seconds, that indicator raises a false alarm for **272 seconds**; its
total is **273**.

The four indicators have four separate ways of erring. **Used swap** is
cumulative: once a page is written to swap, it stays there until read
back, so the indicator stays high even after the pressure ends and shows
the past as if it were today; its false alarm reaches 272 seconds. **Swap
rate** is instantaneous, and at a five-second interval it misses only 2
seconds and raises 13 false alarms; when the interval rises to sixty, the
missed count jumps to 49, because the spike lasts fifteen seconds and
falls between two looks. **Free memory** alarms almost the whole time —
451 seconds — because the cache fills the free space, and free memory sits
near zero even on a healthy system. **Available memory** misses 1 second
and raises 12 false alarms at the five-second interval.

The two lowest totals are 13 and 15, and both depend on the same sampling
interval: when the interval rises to sixty, all four climb past 134. In
the second seed, pressure lasts 86 seconds, and the five-second-interval
totals come out to 271, 16, 469, and 9; the ordering does not change. The
course's rule finds its exact match here: **what distinguishes is not how
many numbers are looked at, but which number is looked at.** The indicator
with "swap" in its name turned out to be the worst one for finding swap
pressure.

## The Cost of Turning It Off

Turning off swap is a one-line command, and it has a one-line risk: every
page in the area has to be **read back** into physical memory. If there is
not enough room, the operation either fails or ends with a process being
terminated for lack of memory.

- **ST36** — Turning off is possible when used swap fits into available
  memory. The oracle makes this comparison every second.
- **ST37** — The diagnosis tested is the common one: **if usage
  percentage is under half, it can be turned off.**

```python
"""To turn off swap, every page in it must be read back into memory."""
SEED = 20260218
PERIOD = 600
PHYSICAL = 1200
BASELINE = 900
AMPLITUDE = 60


def generator(seed):
    d = seed

    def next_value(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return next_value


def demand(seed=SEED):
    r = generator(seed)
    values = []
    for t in range(PERIOD):
        if 200 <= t < 320:
            base = BASELINE + (t - 200) * 6
        elif 480 <= t < 495:
            base = 1700
        else:
            base = BASELINE
        values.append(base + r(2 * AMPLITUDE + 1) - AMPLITUDE)
    return values


def state(seed=SEED):
    used, available, peak = [], [], 0
    for d in demand(seed):
        peak = max(peak, max(0, d - PHYSICAL))
        used.append(peak)
        available.append(PHYSICAL - min(d, PHYSICAL))
    return used, available


def tally(size, seed=SEED):
    used, available = state(seed)
    actual = diagnosed = wrong = 0
    for t in range(PERIOD):
        g = used[t] <= available[t]              # oracle: do the pages fit back in memory
        y = used[t] / size < 0.50                 # diagnosis: usage under half
        actual += g
        diagnosed += y
        wrong += g != y
    return actual, diagnosed, wrong


print("swap size  closable seconds  closable per DIAGNOSIS  wrong diagnosis")
for size in (512, 1024, 2048, 4096):
    g, t, y = tally(size)
    print(f"{size:9d}  {g:16d}  {t:21d}  {y:11d}")
print("observation window:", PERIOD, "seconds")
print()
for seed in (SEED, 20260219):
    print(f"seed {seed}: " + " | ".join(
        f"size {size}: wrong {tally(size, seed)[2]}" for size in (512, 4096)))
```

```
swap size  closable seconds  closable per DIAGNOSIS  wrong diagnosis
      512               246                    290           44
     1024               246                    483          237
     2048               246                    600          354
     4096               246                    600          354
observation window: 600 seconds

seed 20260218: size 512: wrong 44 | size 4096: wrong 354
seed 20260219: size 512: wrong 45 | size 4096: wrong 355
```

Three numbers side by side. **The oracle:** swap can be safely turned off
in **246** of six hundred seconds. **The tool's output:** in a 2048-page
swap area, usage never rises above fifty percent, meaning the indicator
says "fine" in **600 of the window's 600 seconds**. **Wrong diagnosis:**
the same rule is mistaken 44 times at 512 pages, and **354** times at 2048
and above. In the second seed, these two numbers come out to 45 and 355.

The result runs against intuition, and precisely for that reason it is
instructive: **growing the swap area makes the percentage-based diagnosis
worse.** The same 533 pages appear as nearly all of a small area, an
eighth of a large one; the percentage changes even though the page count
does not. A percentage is a ratio, and the ratio's denominator here is a
number the operator chose. The right question is not "how full is swap"
but "do the pages inside it fit back into memory," and the number that
answers that question is not in swap's output but in memory's.

## How Much It Should Be and What Not to Touch

Swap's size is not a correctness decision but a budget decision, and it
has a cost at both ends. On a system with no swap at all, a process gets
terminated the moment demand exceeds physical memory; the failure is
hard, fast, and easy to diagnose. On a system with generous swap, the same
demand produces no termination, but pages go to disk and every read-back
adds a wait; the failure is soft, slow, and hard to diagnose. The choice
between the two is made according to which failure is manageable.

There is also swap tendency: the system carries a setting that determines
how willing it is to write rarely used pages to swap even without memory
pressure. Raising this setting pushes the used-swap indicator above zero
even when there is no pressure, and grows the false-alarm column in the
table above even further.

The destructive-command boundary applies in two places here. First, the
command that formats a partition as swap **irreversibly** destroys the
file system on that partition; if the wrong partition name is written, it
runs without a warning. Second, turning off swap while under pressure can
lead to process termination. This lesson does not give either command in
a runnable, complete form. The safe way to test is verifying the target
partition's identity beforehand, doing the trial with swap built on a
file, and attempting the turn-off only at a moment when used swap is
below available memory.

## Summary

- Swap can be a partition or a file, and it appears at no mount point; the
  mechanics of paged memory are not repeated in this course, what is
  measured is what the operator can see.
- Real memory pressure in the six-hundred-second window is 84 seconds;
  even read once every five seconds, the used-swap indicator raises a
  false alarm for 272 seconds, because a page written to swap stays there
  until it is read back.
- Free memory raises a false alarm for 451 seconds, because the cache
  fills the free space; available memory gives the lowest total at the
  five-second interval, with 1 miss and 12 false alarms.
- Swap rate misses 2 seconds and raises 13 false alarms at the same
  interval, but when the interval rises to sixty, the missed count jumps
  to 49; at coarse sampling, all four indicators climb past 134.
- Swap can be safely turned off in 246 of the window's 600 seconds; the
  rule that looks at usage percentage is mistaken 44 times at 512 pages,
  354 times at 2048 and above — growing the area makes the
  percentage-based diagnosis worse.

## Next Step

Throughout this topic, storage was always something that already existed:
a device, partitions on it, file systems inside them. The course's last
lesson returns to the start of the chain and follows a new device joining
the system from start to finish — recognition, partitioning, formatting,
mounting, making it persistent, and extending it. At every step a tool
says "done," and a layer above is not yet aware of it; what will be
measured is how many steps of the chain give a wrong "the work is done"
diagnosis. The lesson also closes the course.
