Programs use two main kinds of memory: the stack for call frames and local variables, and the heap for dynamically allocated data whose lifetime is not tied to a call. The stack is fast and reclaimed automatically; the heap is flexible and requires an allocator and explicit or garbage-collected release.
Before you start
You should understand processes and virtual memory at a high level. This article covers allocation strategy and its failure modes.
Step-by-step walkthrough
Step 1: Know which region you are using
Stack allocation happens as functions are called and returns automatically when they return, in LIFO order. Heap allocation is requested from an allocator and freed when the program decides, so it can outlive the call. Choosing the stack for short-lived locals and the heap for shared or long-lived data is the first decision.
Step 2: Understand what the allocator does
The allocator requests pages from the OS and hands out smaller blocks, tracking free space. Repeatedly allocating and freeing different sizes leaves gaps, so fragmentation can prevent a large allocation even when total free memory is enough. Allocators use size classes and arenas to limit this.
Step 3: Manage lifetime correctly
Heap memory is released manually in languages like C and automatically by a collector in languages like Java and Go. Manual release risks leaks (never freed) and use-after-free (freed then used), while collection trades throughput for safety. Know which model your language uses.
Worked scenario
The heap block outlives nothing but must be freed explicitly.
int *values = malloc(10 * sizeof(int));
if (values == NULL) { /* allocation failed */ }
values[0] = 1;
free(values);
/* values now dangles; using it is a use-after-free */Walk through the example
malloc returns a heap block whose size is known to the allocator, so free needs only the pointer. Failing to check for NULL risks a dereference of nothing; failing to call free leaks; using values after free is undefined behavior. The allocator cannot detect a dangling pointer, which is why these bugs are dangerous.
Common mistake
Leaking heap memory in a long-running loop until the process is killed, or using memory after freeing it, which corrupts unrelated data. Another is assuming malloc returns zeroed memory; it does not.
Verify the behavior
Run a leak detector or heap profiler and confirm allocations are balanced with frees. Request a large block after many small frees and observe whether fragmentation prevents it. Access a freed pointer under a sanitizer and confirm it is reported.
Interview exercise
Why can a program run out of memory even with free memory available?
Answer and reasoning
Because fragmentation can split free memory into gaps too small for the requested block. Even if the total free bytes exceed the request, no single contiguous region may be large enough, so the allocation fails. Allocators mitigate this with size classes and arenas, and compaction helps where the language allows it.
Continue learning
Compare memory topics in Stack and heap and Virtual memory. Read the Linux malloc documentation and try the Operating systems interview questions.