Skip to content
QSWEQB
mediumConceptMidSenior

What is virtual memory, and what happens when a process touches a page that is not resident?

Virtual memory gives each process a private address space that the MMU translates to physical frames, so processes are isolated and memory is committed lazily. A missing page traps to the kernel, which either maps a frame cheaply or reads from disk - a difference of five orders of magnitude.

4 min readUpdated 2026-07-26

What the interviewer is scoring

  • Does the candidate separate the isolation purpose from the overcommit purpose
  • Whether they distinguish a minor fault from a major one and know the cost gap
  • That the TLB is named as the reason translation is not ruinously slow
  • Whether they connect thrashing to a working set exceeding physical memory
  • Does the candidate explain why an allocation can succeed and the write still fail

Answer

Two problems it solves

Virtual memory is usually explained as a way to pretend you have more RAM than you do, which undersells it. The first thing it buys is isolation: every process addresses memory starting at zero, and those addresses mean nothing outside that process, so one program cannot read or corrupt another's memory by construction rather than by convention. That property is what makes a multi-user operating system possible at all, and it would be worth the machinery even on a machine that never ran short of RAM.

The second is indirection, and everything else follows from it. Because the process's view of memory is translated rather than direct, the kernel is free to place a page anywhere in physical memory, to leave it unbacked until first use, to share one physical copy between processes, or to move it to disk. Fragmentation stops mattering to the program, and physical memory can be allocated lazily.

How a virtual address becomes a physical one

The address space is divided into fixed-size pages, conventionally 4 KiB on x86-64 and 4 KiB or 16 KiB on 64-bit ARM depending on configuration. Physical memory is divided into frames of the same size. A page table maps one to the other, and the MMU walks it in hardware on every memory access.

A full walk is expensive because the table is hierarchical — four levels on x86-64, so a translation is potentially four extra memory reads before the read you wanted. What makes this workable is the TLB, a small cache of recent translations. Hit rates are high because programs have locality, and a hit turns translation into essentially nothing. A miss costs the walk. This is why the page size is a performance knob: huge pages cover far more address space per TLB entry, which is why databases and JVMs with large heaps often ask for them.

Minor and major faults

When the MMU finds no valid translation, it raises a page fault and the kernel takes over. Whether that is trivial or catastrophic depends on what the kernel finds.

A minor fault means the page is not mapped for this process but no disk access is needed. Perhaps this is the first write to a freshly allocated page and the kernel simply hands over a zeroed frame. Perhaps the page is already in the page cache because another process has it open. Perhaps it is a copy-on-write page after a fork and the kernel copies a frame. All of this is a trap into the kernel and back — hundreds of nanoseconds to a few microseconds.

A major fault means the data must be read from storage. On an NVMe SSD that is tens of microseconds; on a spinning disk, several milliseconds. Against a roughly 100-nanosecond RAM access, a major fault is four to five orders of magnitude slower, and this gap is the whole reason paging behaviour dominates the performance of a memory-constrained system.

The practical diagnostic follows directly. A process consuming little CPU while the machine shows high iowait, with a major fault count climbing, is not slow because of its code. It is slow because its working set does not fit and it is reading its own memory back from disk.

Lazy allocation, and the surprise it creates

Because pages are only backed when touched, allocating memory and using it are separate events. Requesting a gigabyte typically adjusts the process's address space and nothing more; resident memory does not move until you write to the pages, at which point they arrive one minor fault at a time.

This is why the two memory figures for a process differ so much and mean different things. Virtual size is what has been promised. Resident size is what is actually in RAM. Judging a process by its virtual size will mislead you badly, particularly for anything that memory-maps large files.

Linux takes this further and by default permits overcommit — granting more total address space than it could ever back. Most of the time this is correct, because most programs do not touch everything they allocate. When it is wrong, the failure arrives at the worst possible moment: not at allocation, which succeeded, but at a write to a page the kernel cannot back, with the OOM killer choosing a process to terminate. An allocation that returns success is therefore not a guarantee of memory, and code that carefully checks its allocation result is defending against a failure mode that this policy has moved somewhere it cannot be checked.

When it stops working

Paging degrades gracefully right up until it does not. If the combined working set of running processes exceeds physical memory, the kernel evicts a page that is about to be needed, faults it back, and evicts another — thrashing, where the machine spends its time moving pages rather than executing. Throughput collapses far out of proportion to how much memory you are short, because every fault is a disk round trip and each one displaces another live page.

The tell is a system that is unresponsive while CPU utilisation looks low. Adding CPU will do nothing. The fix is to reduce the working set or add memory, and the reason it feels like a cliff rather than a slope is that the cost per fault is fixed while the fault rate rises without bound.

Minor faults are the cost of doing business and happen constantly. Major faults are disk reads wearing a memory access as a disguise, and a workload that generates them at volume has already lost.

Likely follow-ups

  • Why does allocating a large buffer often not increase resident memory until you write to it?
  • What is the TLB, and what happens to it on a context switch?
  • Your process is slow and the machine shows high iowait with low CPU. What do you suspect?
  • How does memory-mapping a file differ from reading it?

Related questions

virtual-memorypagingmmupage-faultoperating-systems