AlgoMaster Logo

Working Sets and Thrashing

35 min readUpdated August 7, 2026
Listen to this chapter
Unlock Audio

A service has a 20 GiB virtual address space and an 8 GiB resident set, but neither number directly answers the most important performance question:

How much memory does the service need for the pages it is actively using now?

A process rarely uses every mapped page with equal frequency. It moves through phases: parsing input, updating an index, running a query, serializing a response, or performing background maintenance. Each phase repeatedly touches a smaller collection of pages.

That actively used collection is the process's working set.

When physical memory can hold the working sets of active workloads, most references hit resident pages and execution makes progress. When it cannot, the operating system repeatedly evicts pages that are needed again almost immediately. Fault handling and data movement begin to dominate useful work.

That failure mode is thrashing.

Thrashing occurs when a workload has too few frames for its active pages and spends most of its time faulting, reclaiming, and restoring pages instead of executing useful instructions.

Working sets explain why a process can have a huge address space yet run efficiently, while a much smaller process can perform terribly under memory pressure.

Locality-Derived Working Sets

Programs exhibit locality: references cluster around a subset of code and data for a period.

Consider a request handler:

The pages used during parsing differ partly from those used during query execution. Within each phase, many references repeatedly return to the same pages.

Two forms of locality matter:

  • Temporal locality: a page referenced recently is likely to be referenced again.
  • Spatial locality: addresses near a recent reference are likely to be used soon.

Page replacement algorithms exploit these patterns by trying to retain recently active pages. The working-set model makes the active collection explicit.

A working set is not a permanent property of a program. The same process can move from a 50 MiB active set during idle request handling to several gigabytes during an analytical query, then shrink again when that phase ends.

Defining the Working Set

Let:

denote the set of distinct pages referenced during the window of Δ references ending at time t.

The working-set size is:

where |W| means the number of distinct pages in the set.

If the page size is uniform:

For example, a working set of 12,000 pages with 4 KiB pages occupies approximately:

The reference window is a conceptual measure of recent activity. It can be expressed as:

  • The most recent Δ memory references
  • All page references during the most recent time interval
  • A practical approximation based on periodically sampled page-access state

The original model uses a reference-count window. Operating systems and monitoring tools often use time because they cannot cheaply record every memory reference.

Calculating a Working Set

Consider this page-reference sequence:

Use a window of the five most recent references:

At position 8, the window contains positions 4 through 8:

At position 10, the window crosses a phase transition:

At position 14:

The process appears to move from a locality using pages {1, 2, 4} to another using {5, 6, 7}. During the transition, the window temporarily includes pages from both phases, so the measured working set expands.

This is useful behavior, not a flaw. A process often needs extra frames while moving between localities because it is still using some old pages while beginning to use new ones.

Choosing the Window Size

The working set depends on Δ.

If the window is too small, it misses part of the current locality:

If the window is too large, it retains pages from old phases:

Consider a page used once during startup and never again. A very large window can keep it in the measured working set long after it has stopped helping current execution.

There is no universal best window. An interactive service, batch analytics job, compiler, and database can have different locality durations. A process can also change its behavior over time.

Practical memory management therefore uses approximations and adaptive observations rather than relying on one perfect fixed value of Δ.

The definition remains valuable because it clarifies the goal:

Keep pages that belong to the workload's recent locality; reclaim pages that have fallen outside it.

Working Set vs. RSS

The resident set size, or RSS, measures how much of a process is currently resident in physical memory according to the operating system's accounting.

The working set measures which pages were actively referenced during a chosen recent window.

They can differ:

A process can have 8 GiB of RSS but actively reuse only 2 GiB. The remaining resident pages are potential reclaim candidates.

Another process can have 2 GiB of RSS while cycling through a 4 GiB active set. Its current resident memory is smaller, but its memory performance can be much worse because useful pages continually displace one another.

RSS is directly observable. A precise working set is harder to measure because it depends on a time window and a record of references. Operating systems estimate activity from referenced bits, recency groups, refault behavior, and other evidence.

Shared pages add another complication. Summing per-process working-set estimates can double-count a physical frame actively used by multiple processes. Capacity planning must distinguish virtual activity from unique physical demand.

From a Working Set to Frame Demand

Suppose three active processes have estimated working-set sizes:

Their combined demand is:

If the operating system has 14 eligible frames for these processes, their working sets can fit with two frames available for transitions and other needs.

If only 10 frames are available:

Some actively used pages must remain nonresident. Under global replacement, one process faults in a page by evicting an active page from itself or another process. The victim is soon referenced, causing another fault and another eviction.

The general capacity condition is:

If:

the active localities can fit in principle.

If:

the system cannot keep every active working set resident simultaneously. Replacement policy can change which faults occur, but it cannot remove the capacity shortfall.

A Minimal Thrashing Example

Consider a process that repeatedly cycles through four pages:

Give it three frames and use LRU.

The first three references fill the frames:

Reference 4 evicts page 1, which is least recently used:

The next reference is 1, so page 1 must return and page 2 leaves:

Then 2 evicts 3, 3 evicts 4, and 4 evicts 1. After warm-up, every access faults.

The active set is:

but only three frames are available.

Give the process four frames, and the behavior changes:

The difference between three and four frames is not a small percentage improvement. It is the difference between almost no useful residency and a stable working set.

This sharp boundary is why memory performance can collapse suddenly as pressure increases.

The Thrashing Loop

Thrashing is self-reinforcing.

The kernel spends CPU time scanning reclaim candidates and updating mappings. Dirty pages may require write-back. Faulting threads wait for contents to return. Storage queues grow. Pages can be fetched only to be evicted before doing much useful work.

The defining characteristic is not merely a high fault count. It is repeated loss and rapid reuse of pages that belong to active localities.

A one-time burst of minor faults while a service initializes is not thrashing. Nor is one major fault for a cold executable page. Thrashing persists because the available resident capacity remains below ongoing demand.

Loading simulation...

The Multiprogramming Trap

Multiprogramming normally improves CPU utilization. When one process waits for I/O, another can run.

The benefit holds only while active processes have enough memory to make progress.

As more processes become active:

A classic utilization curve looks like:

An operating system that observes low CPU utilization and responds by admitting still more memory-demanding processes can make the problem worse. Faulting processes are blocked on page-ins, so the CPU appears underused even though the real bottleneck is memory and storage.

On modern multicore systems, CPU utilization does not always fall visibly. CPUs may remain busy in kernel reclaim, page-table updates, compression, or I/O completion. The more reliable signal is falling useful throughput combined with rising memory stalls and fault or reclaim activity.

Page-Fault Frequency Control

The working-set model asks which pages were recently used. A related control strategy watches the page-fault frequency, or PFF, of each process.

The idea uses two thresholds:

A conceptual feedback policy is:

If a process faults too frequently and free frames exist, increase its resident allocation. If no frames exist, taking pages from another process may simply move the problem. The system may need to delay, suspend, or limit some workload.

If a process has a persistently low fault rate, some of its cold pages can be reclaimed for other work.

PFF is easier to observe than an exact working set, but it is a lagging signal. Faults reveal that needed pages were already absent. A sudden locality change can create a burst before the controller adapts.

Why Replacement Policy Alone Cannot Fix Thrashing

A better victim algorithm reduces avoidable faults. It cannot keep four simultaneously active pages in three frames.

Even the theoretical Optimal algorithm faults repeatedly when capacity is fundamentally insufficient for the future reference pattern.

Replacement policy matters most when there are genuinely colder and hotter pages to distinguish. During severe thrashing, nearly every candidate may belong to an active locality.

The problem changes from:

to:

That is an admission-control and resource-allocation decision, not merely an LRU-versus-Clock decision.

This also explains why faster storage is not a complete cure. It makes each page-in or write-back cheaper, but the system still performs repeated work and remains much slower than keeping the active pages in RAM.

Local and Global Thrashing

Thrashing can occur at different scopes.

One process

A single process cycles through an active set larger than the frames available to it. Other processes may remain healthy.

The whole machine

Combined active working sets exceed physical capacity. Global reclaim moves pressure among processes, storage traffic rises, and system-wide responsiveness deteriorates.

A container or memory-control group

A container can thrash against its memory limit even when the host has unused memory outside that limit.

The container's active pages compete within the policy boundary. Reclaim and refault can repeat locally while unrelated host workloads remain unaffected.

This is a critical backend diagnostic rule: always identify the memory boundary being enforced. Host-level free memory does not prove that a constrained service has enough frames for its working set.

Bad Reclaim Decisions Revealed by Refaults

A refault occurs when a page is reclaimed and then accessed again, causing it to return.

Not every refault is a problem. A page can be cold for a long time, be reclaimed reasonably, and become useful again during a later phase.

A rapid refault is more informative:

This suggests that reclaim selected a page still belonging to the active locality.

A high rapid-refault rate means the system is recycling useful contents rather than finding truly cold pages. It can help distinguish:

Operating systems can use refault distance or recency evidence to adjust which pages receive protection. Monitoring tools may expose reclaim and refault-related counters, though exact names and availability are system-specific.

Observing Memory Pressure on Linux

No single metric proves thrashing. Look for a correlated pattern.

Page-fault rates

Per-process fault rates are visible with:

Typical fields include minor and major faults per second, virtual size, and resident size. Field names vary by tool version.

For one command:

This gives cumulative minor and major faults for the process lifetime.

Major faults are particularly expensive because they require storage I/O. A high minor-fault rate can also matter when it reflects repeated mapping work rather than one-time initialization.

Swap and blocked work

Run:

Useful columns commonly include:

Sustained swap-in and swap-out alongside poor application throughput is a strong warning sign. A system can still suffer destructive file-page reclaim without swap activity, so zero si and so do not rule out memory pressure.

Kernel virtual-memory counters

System-wide cumulative counters are available in /proc/vmstat:

Read them at two times and calculate rates. The raw totals include activity since boot.

pgscan-related counters show reclaim scanning. pgsteal-related counters show pages reclaimed. Exact suffixes depend on kernel configuration and memory domain.

Pressure Stall Information

On Linux systems that support Pressure Stall Information:

The some line reports periods when at least one task was delayed by memory pressure. The full line reports periods when all non-idle work was stalled on memory pressure.

For a cgroup v2 workload, its local pressure may be available in the cgroup's memory.pressure file. This is useful when host-wide averages hide one constrained container.

PSI measures time lost to pressure, not working-set size directly. It becomes most useful when correlated with fault, reclaim, swap, latency, and throughput data.

Recognizing the Pattern

Thrashing usually presents as several symptoms moving together:

SignalExpected pattern under thrashing
Useful application throughputFalls sharply
Request or job latencyRises, often with long tails
Major faults or refaultsSustained increase
Reclaim scanningHigh and persistent
Swap or file-page I/OHigh when backing reads/writes are needed
Memory pressure stallsSustained
Resident pagesChurn rather than stabilize

CPU utilization needs careful interpretation. It may fall because threads block on storage, or stay high because the kernel is busy reclaiming and copying memory. High CPU is not proof of productive work.

One-time transitions should be separated from a steady loop:

Measure long enough to see whether the workload reaches a stable warm state.

Reducing Thrashing

The remedy must change capacity, active demand, or locality.

Increase available physical memory

More frames can let the active working sets fit. In a constrained container, this may mean raising the memory limit rather than adding host RAM.

Reduce concurrent memory demand

Limit the number of memory-heavy jobs, requests, workers, or processes active at once. Queueing some work can produce higher total throughput than running everything simultaneously in a fault storm.

Reduce the application's working set

Release caches that are not providing value, stream data instead of materializing it all, process large datasets in locality-friendly chunks, and avoid touching memory that is reserved but not needed.

Improve access locality

Compact data structures, group related objects, traverse data sequentially where possible, and batch operations that use the same pages. Fewer distinct active pages reduce frame demand.

Isolate workloads

Use appropriate resource boundaries so one service cannot reclaim all useful pages from another. Isolation must be paired with realistic limits; a limit below a service's unavoidable working set causes local thrashing.

Warm selectively

Pre-touching latency-critical pages can reduce first-request faults. It cannot solve a working set larger than available memory; warming too much can trigger pressure sooner.

Investigate leaks and unbounded caches

A growing resident footprint can push previously healthy working sets into competition. Fixing the source is better than tuning replacement around continuous growth.

Adding swap or faster backing storage can prevent immediate failure and reduce individual fault cost. It does not make repeated page movement equivalent to RAM-speed execution.

Working-Set-Aware Capacity Planning

Peak virtual size is usually a poor capacity input. Peak RSS is better, but it can include cold resident pages and one-time phases.

A useful capacity study records:

Increase workload gradually. A stable system shows faults settling after warm-up and throughput scaling until another resource becomes limiting.

A working-set boundary often appears as a knee:

Provision headroom for phase transitions, operating-system memory, shared services, and workload variance. Configuring total expected working sets to equal every available byte leaves no room for bursts or reclaim inefficiency.

For shared pages, avoid blindly summing per-process RSS or working-set estimates. The physical system needs one resident shared frame even if many address spaces reference it.

Working-Set Approximations

Recording every page reference would be too expensive. Operating systems approximate recency using limited hardware and periodic sampling.

A simple aging model can periodically shift a counter for each candidate page and insert the current reference bit:

Pages referenced in recent intervals retain larger values. Pages not referenced for several intervals decay toward zero and become stronger reclaim candidates.

Another design groups pages into a small number of recency generations rather than maintaining an exact timestamp for every access. Recently observed pages enter younger groups; pages that age without reuse move toward reclaimable groups.

These mechanisms do not reproduce the mathematical W(t, Δ) exactly. They answer a cheaper practical question:

Refault feedback can correct mistakes. A page reclaimed and immediately needed again was probably treated as colder than it really was.

Approximation is necessary because tracking overhead competes with the application for CPU time and memory bandwidth.

Summary

A working set is the collection of distinct pages referenced during a recent window. It represents a workload's active memory demand more accurately than virtual size or current RSS alone, and it changes as the process moves between localities.

When combined active working sets fit in available frames, demand paging and replacement can keep useful pages resident. When they do not fit, active pages evict one another and refault quickly. Sustained fault handling, reclaim, write-back, and page-in work replace useful execution; this is thrashing.

Replacement policy can reduce avoidable mistakes but cannot fix a fundamental capacity shortfall. Effective remedies increase available memory, reduce concurrent demand, shrink the active set, improve locality, or enforce realistic workload boundaries. Diagnosis should correlate faults, reclaim, storage activity, pressure stalls, throughput, and latency at the actual host or container memory boundary.

Quiz

Working Sets and Thrashing Quiz

5 quizzes