100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

The Heap and Dynamic Memory

How assembly programs request and manage runtime memory using brk/sbrk and mmap, and the manual bookkeeping needed to avoid leaks.

Memory & AddressingAdvanced10 min readJul 10, 2026
Analogies

The Heap and Dynamic Memory

Unlike stack memory, which is automatically reclaimed when a function returns, and static memory declared in .data/.bss, which exists for the program's entire lifetime, heap memory is requested and released explicitly at runtime, giving the program control over allocation sizes and lifetimes that aren't known until the program is actually running. On Linux, the two lowest-level mechanisms to grow the heap are the brk/sbrk syscalls, which extend a single contiguous program break, and mmap, which asks the kernel for an entirely separate mapped region, typically used for larger allocations.

🏏

Cricket analogy: A team management calling up a player from the reserves only when an injury happens mid-match, rather than fielding a fixed squad decided before the toss, mirrors heap allocation happening on demand at runtime rather than being fixed in advance.

brk/sbrk and mmap

The brk syscall sets the program break — the end of the process's data segment — to an absolute address, while sbrk (a libc wrapper, not a syscall itself) adjusts it by a relative increment and returns the previous break address, which is exactly where the newly available memory begins. Because brk only ever moves one boundary up or down, it is inherently a single contiguous region and cannot easily return memory to the OS except by shrinking from the very top, which is why general-purpose allocators built on brk must manage free lists internally rather than repeatedly calling brk for every small allocation.

🏏

Cricket analogy: Extending a single boundary rope marking the edge of a training ground, rather than pitching separate tents elsewhere, mirrors brk's approach of growing one contiguous region rather than scattering separate allocations.

nasm
; minimal heap allocation using the brk syscall (Linux x86-64)
section .text
global _start

_start:
    ; find current break: brk(0)
    xor     rdi, rdi
    mov     eax, 12          ; sys_brk
    syscall
    mov     r12, rax         ; r12 = current break (start of new memory)

    ; grow the heap by 4096 bytes
    lea     rdi, [r12 + 4096]
    mov     eax, 12          ; sys_brk
    syscall                  ; rax = new break on success

    ; r12 now points to 4096 usable bytes
    mov     dword [r12], 0x12345678   ; use the freshly allocated memory
    mov     eax, [r12]

    mov     eax, 60
    xor     edi, edi
    syscall

mmap with MAP_ANONYMOUS|MAP_PRIVATE is generally preferred over brk for allocations above a few hundred KB, because mmap'd regions can be independently unmapped with munmap and returned to the OS immediately, whereas brk-based heap space can typically only shrink from the very top of the single contiguous region.

Manual Allocator Bookkeeping and Leaks

A minimal heap allocator built on top of brk needs its own bookkeeping: each allocated block is typically prefixed with a small header storing at least its size (and often a free/used flag and pointers to neighboring blocks), so that a corresponding 'free' operation knows how many bytes to reclaim and can potentially merge adjacent free blocks to fight fragmentation. Since assembly has no automatic garbage collector, forgetting to free a block — or losing the only pointer to it by overwriting the register/variable holding its address — permanently leaks that memory for the remainder of the process's life.

🏏

Cricket analogy: A scorer's ledger noting exactly how many overs each substitute fielder is registered for, so they can be correctly released back to the reserve bench afterward, mirrors an allocator header tracking a block's size for later freeing.

Losing the only pointer to a heap block — by overwriting the register or stack slot holding its address before calling free/munmap — makes that memory permanently unreachable for the rest of the process's life. Since assembly has no garbage collector, long-running programs with this bug slowly exhaust available memory, a classic and often hard-to-diagnose memory leak.

  • Heap memory is requested and released explicitly at runtime, unlike automatically-managed stack memory or fixed-lifetime static memory.
  • brk sets the program break to an absolute address; sbrk (a libc convenience wrapper) adjusts it by a relative amount and returns the old break.
  • brk manages one single contiguous region, so it generally can only shrink from the top, making it awkward for returning arbitrary freed memory to the OS.
  • mmap with MAP_ANONYMOUS creates independent mapped regions well suited to large allocations, since each can be unmapped separately with munmap.
  • A hand-built allocator typically stores a header (at least a size field) before each block so a later free operation knows how much memory to reclaim.
  • Assembly has no garbage collector; every allocation must be explicitly freed, and losing the only pointer to a block leaks that memory permanently.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#AssemblyLanguageStudyNotes#TheHeapAndDynamicMemory#Heap#Dynamic#Memory#Brk#DataStructures#StudyNotes#SkillVeris