Section 3.5 gave a quick preview of this layout. Here we look at each region in detail. This is the inner structure of every running program's virtual address space.

Analogy: Think of a workshop with different areas. One shelf holds the instruction manual (code). One holds the labelled tool boxes that stay all day (data). One is the messy table where you spread out whatever the job needs (heap). One is a neat stack of to-do notes (stack).

High addresses  ┌───────────────────────────────┐
                │   Kernel-mode memory          │
                ├───────────────────────────────┤
                │   STACK  (grows downward ↓)   │
                ├───────────────────────────────┤
                │        (unused gap)           │
                ├───────────────────────────────┤
                │   HEAP   (grows upward ↑)     │
                ├───────────────────────────────┤
                │   Loaded DLLs                 │
                ├───────────────────────────────┤
                │   DATA SEGMENT (globals)      │
                ├───────────────────────────────┤
                │   CODE SEGMENT (instructions) │
Low addresses   └───────────────────────────────┘

(A DLL, "Dynamic Link Library", is a file of ready-made code that many programs share, such as Windows' own helper routines.)

6.1 Stack (Function Calls, Local Variables)

What it is: A region of memory for function calls, local variables, and remembering "where to go back to". It works as a Last-In-First-Out (LIFO) pile: the last thing put on is the first thing taken off, like a stack of plates.

Each time a function is called, a new stack frame is pushed on. It holds that function's local variables, its inputs (parameters), and the return address (where to carry on once the function finishes). When the function returns, its whole frame is popped off and thrown away.

main() calls A(), which calls B():

┌───────────────────┐  ← top of stack (grows this direction)
│  B's stack frame  │     (B's locals, return address into A)
├───────────────────┤
│  A's stack frame  │     (A's locals, return address into main)
├───────────────────┤
│ main's stack      │
│     frame         │
└───────────────────┘  ← bottom of stack

Key points:

  • Each thread in a process gets its own stack. This links to Chapter 2's talk about threads: every thread needs its own place to track its own function calls.
  • Stack memory is cleaned up automatically the instant a function returns. No manual cleanup.
  • Stack size is usually fixed and small (1 MB is a common Windows default per thread). Going past it causes a stack overflow (see Section 8.4).

6.2 Heap (Dynamic Allocation)

What it is: A region for data whose size or lifetime is not known when the program is built. The program asks for this memory while running, and (in "unmanaged" languages) must hand it back by itself when done.

Unlike the strict, automatic order of the stack, heap memory can be taken and given back in any order, at any time. It can last as long as the program needs it, even across many function calls. Think of a car park: cars arrive and leave in any order, and each takes whatever space it needs.

Heap allocation example (conceptually):

ptr = allocate(1000 bytes)   ← heap grows to satisfy the request
... use the memory via ptr ...
free(ptr)                    ← memory returned to the heap's free pool

More in Section 7.

6.3 Data Segment (Global Variables)

What it is: A region for global and static variables. These live for the whole life of the program, are not tied to any one function call, and are not requested while running.

This segment is often split into two parts:

PartWhat it holds
Initialized dataGlobals that start with a value written in the source code
Uninitialized data (BSS)Globals with no starting value; the OS guarantees they are zeroed before the program starts

6.4 Code Segment (Instructions)

What it is: The region that holds the program's compiled machine instructions (the kind of instructions from Chapter 2, Section 8). This is what the CPU's Program Counter steps through during the fetch stage.

This segment is normally mapped read-only and executable, using the per-page permission bits from Section 4.6. The program can run its own code, but nothing — not even the program's own bugs — may change it while it runs. It is also why one executable file can be safely shared, read-only, by many running copies of the same program.

6.5 Memory Layout Diagram — Putting It Together

┌───────────────────────────────────────────────────────────┐
│  KERNEL MEMORY   (shared across all processes, protected) │
├───────────────────────────────────────────────────────────┤
│  STACK           per-thread, LIFO, function calls         │
│      ↓ grows toward heap                                  │
├───────────────────────────────────────────────────────────┤
│  (free space between stack and heap, can grow into)       │
├───────────────────────────────────────────────────────────┤
│      ↑ grows toward stack                                 │
│  HEAP            dynamic allocation (malloc/new)          │
├───────────────────────────────────────────────────────────┤
│  LOADED DLLs     shared libraries mapped into the process │
├───────────────────────────────────────────────────────────┤
│  DATA SEGMENT    global/static variables                  │
├───────────────────────────────────────────────────────────┤
│  CODE SEGMENT    compiled instructions (read-only + exec) │
└───────────────────────────────────────────────────────────┘

Why this layout matters: The stack grows one way and the heap grows the other way, with free space between them. This is a deliberate design: either one can expand as needed, without a fixed and wasteful split decided in advance. It also matters for security: a stack that overflows its limits, or a heap corruption bug, behaves in a predictable way because the layout is consistent. That predictability is what makes both attacking and defending against these bugs possible to reason about (more in Section 8).

Summary

RegionWhat it holdsKey point
StackFunction calls, locals, return addressesLIFO, automatic cleanup, about 1 MB per thread
HeapMemory requested while runningAny order, any lifetime, you must free it (unmanaged)
Data segmentGlobal/static variablesInitialized data + BSS (zeroed)
Code segmentCompiled instructionsRead-only and executable; shareable
Loaded DLLsShared librariesMapped into the process
Kernel memoryOS memoryShared by all processes, but protected