Segmentation

Intermediate
Memory Management

Segmentation divides a process's address space into variable-size logical segments (code, data, heap, stack), each with independent base, limit, and protection attributes.

Overview

Unlike paging (which divides memory into fixed-size pages with no logical meaning), segmentation reflects the programmer's view of memory: a program is code, data, a heap, and a stack — each a meaningfully distinct region. The segment table maps segment numbers to a base address and limit. When a memory access is made, the segment number selects the entry, the offset is checked against the limit (to catch out-of-bounds), and the physical address is base + offset. Each segment can have independent permissions: code segments are read+execute only, data segments are read+write, and stack segments can be marked non-executable (NX bit, exploited by stack overflow attacks). The main drawback is external fragmentation — variable-size segments leave awkward holes in physical memory. Modern x86-64 Linux and Windows use flat segmentation (all segment bases = 0, limits = max) combined with paging, effectively using only paging for address translation.

Segment Table Lookup and Protection

Each segment table entry contains: base (physical start address), limit (maximum valid offset), and protection bits. When a program accesses address [segment:offset], the MMU checks offset < limit; if not, a segmentation fault is raised. This is the hardware mechanism behind Java's array bounds checks — though Java implements bounds checking in software, the OS also catches truly wild pointer dereferences.

Java — segment table simulation with bounds check and protection
// Segmentation: logical address = [segment number, offset]
// Physical address = segment_base + offset  (if offset < segment_limit)

// Simulated segment table
record Segment(int base, int limit, String permission) {}

Segment[] segmentTable = {
    new Segment(0x1000, 0x0FFF, "rx"),   // seg 0: code (read+exec)
    new Segment(0x5000, 0x1FFF, "rw"),   // seg 1: data (read+write)
    new Segment(0x8000, 0x3FFF, "rw"),   // seg 2: heap (read+write)
    new Segment(0xF000, 0x1FFF, "rw"),   // seg 3: stack (read+write)
};

// Translate logical address [segment=1, offset=0x100]
int segNum = 1;
int offset = 0x100;
Segment seg = segmentTable[segNum];

if (offset > seg.limit()) {
    System.out.println("Segmentation fault: offset exceeds limit!");
} else {
    int physicalAddr = seg.base() + offset;
    System.out.printf("Logical [%d:0x%X] → Physical 0x%X  (perm: %s)%n",
        segNum, offset, physicalAddr, seg.permission());
}

// Attempt to write to code segment → protection violation
segNum = 0; offset = 0x10;
System.out.println("Code segment permission: " + segmentTable[segNum].permission());
// A write attempt to seg 0 (rx) would fault — OS enforces this via segment permissions

External Fragmentation and Compaction

Because segments are variable in size, physical memory fills up with gaps between segments that are too small to satisfy new allocation requests — external fragmentation. Compaction (moving all segments to pack them together) solves this but is expensive: it requires pausing the process, copying memory, and updating all segment base addresses. This is analogous to Java GC compaction in old-gen collectors like CMS.

Java — external fragmentation analogy with GC collectors
// External fragmentation illustration:
// Physical memory after several segment alloc/dealloc cycles:
//
// [USED:4KB][FREE:2KB][USED:8KB][FREE:3KB][USED:1KB][FREE:6KB]
//
// Total free = 11 KB, but largest contiguous = 6 KB
// A 7 KB segment request FAILS even though 11 KB is free!
// Solution: compaction (expensive) or switch to paging (eliminates external frag)

// Java GC analogy: CMS (concurrent mark-sweep) does NOT compact
// → old gen gets fragmented over time → promotion failures
// G1GC and ZGC DO compact → avoid fragmentation

// Demonstrating why G1GC was introduced
// java -XX:+UseG1GC   → compacting collector, avoids external fragmentation
// java -XX:+UseConcMarkSweepGC  → non-compacting, can fragment over time (deprecated)

// Checking current GC
List<GarbageCollectorMXBean> gcBeans =
    ManagementFactory.getGarbageCollectorMXBeans();
gcBeans.forEach(gc ->
    System.out.println("GC: " + gc.getName() + " — collections: " + gc.getCollectionCount()));
// G1GC shows "G1 Young Generation" and "G1 Old Generation"

Key Points to Remember

  • 1Segmentation divides address space into variable-size logical segments, each with base + limit + permissions.
  • 2Physical address = segment base + offset; OS raises segfault if offset exceeds limit.
  • 3Each segment has independent protection: code = rx, data/heap/stack = rw.
  • 4External fragmentation: variable-size segments leave unusable holes in physical memory.
  • 5Compaction solves external fragmentation but is expensive (stop, copy, update pointers).
  • 6Modern x86-64 OS use flat segmentation (all bases = 0) combined with paging for all practical isolation.

Interview Questions

Sign in to ask Aria
1

What is the difference between segmentation and paging?

MediumMicrosoft
2

Why does segmentation suffer from external fragmentation but paging does not?

MediumGoogle
3

How does segment-level protection prevent stack overflow exploits?

HardAmazon
4

How does external fragmentation in memory relate to GC fragmentation in Java?

MediumNetflix

Ask Aria about Segmentation

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…