Introduction
A process address space is divided into memory regions because different program data has different roles and lifetimes. Program instructions, global variables, dynamic objects, and function-call data are not managed in the same way.
The main regions are code or text, data, BSS, heap, and stack. The deciding idea is simple: what the data is and how long it lives usually tells us where it belongs.
Process Memory Layout
A running program contains instructions to execute, global state, dynamically allocated objects, and function-call information. The operating system and runtime organize these into separate regions inside the process address space.
The common layout includes:
Code or text: Executable program instructions.
Data: Initialized global and static variables.
BSS: Uninitialized or zero-initialized global and static variables.
Heap: Dynamically allocated memory used during runtime.
Stack: Function-call frames, parameters, return addresses, and local variables.
This layout is a conceptual model. Exact placement can differ by operating system, executable format, architecture, compiler, and security settings.
What lives inside address space
Code, Data and BSS Segments
The code, data, and BSS segments separate program instructions from global or static state. These regions are usually created when the program is loaded.
Segment | Stores | Example |
|---|---|---|
Code/Text | Executable instructions | Program machine code |
Data | Initialized global/static variables |
|
BSS | Zero-initialized or uninitialized global/static variables |
|
The code segment is usually read-only and executable. The program should be able to run its instructions, but accidental writes to the instruction region should be blocked.
The data segment stores global or static variables that already have explicit initial values. The BSS segment stores static-duration variables that begin as zero or have no explicit nonzero value.
Heap Memory
The heap is the dynamic memory region. It is used when a program asks for memory while it is running, and that memory is not tied to a single function call.
Heap memory is used for:
Dynamic allocation: Memory requested through mechanisms such as
mallocornew.Longer lifetime: The memory can outlive the function that allocated it.
Shared process access: Threads in the same process can access heap memory because they share the process address space.
Runtime-sized objects: Structures whose size or lifetime is decided during execution.
The heap is managed by an allocator. Many allocation requests are handled by the runtime allocator first; the allocator asks the operating system for more memory only when it needs more space.
Heap and Stack Memory
Stack Memory
The stack stores function-call data for a thread. Each active function call has a stack frame, and that frame contains information needed while the function is running.
The stack stores function-call information, including function-call frames, return addresses, parameters, local variables, and saved registers or other call-related information.
The stack is about call lifetime. A stack frame is created when a function call begins and is removed when that function returns.
If main calls solve, and solve calls helper, the stack contains frames for those active calls. When helper returns, its frame disappears, and local variables inside that frame are no longer valid.
Stack vs Heap
Stack and heap are often compared because both store program data, but they are used for different lifetimes and managed differently.
Point | Stack | Heap |
|---|---|---|
Lifetime | Automatic, tied to function calls | Dynamic, controlled by program or runtime |
Main use | Call frames and local variables | Runtime allocations and dynamic objects |
Management | Created and removed with calls/returns | Allocated and freed explicitly or by runtime |
Thread behavior | Each thread has its own stack | Threads in a process share the heap |
Example: Function Call and Dynamic Allocation
Suppose a function declares a local variable:
void helper() {
int x = 5;
}The variable x belongs to the stack frame of helper. When helper returns, that frame is removed, so returning a pointer or reference to x would be unsafe.
Now consider dynamic allocation: p = malloc(100)
Here, the allocator finds or requests heap space and returns a pointer to the allocated block. That block remains allocated until the program releases it using free(p) or an equivalent runtime mechanism.
Common Bugs
The most common stack-related mistake is using local stack data after its function has returned. The local variable belonged to a stack frame that no longer exists.
Common heap bugs include:
Memory leak: Allocated memory is never released.
Use-after-free: Code uses memory after it has been freed.
Double free: The same allocation is freed more than once.
These bugs come from misunderstanding lifetime. Stack data disappears automatically with function returns, while heap data must be managed according to its allocation rules.
Summary
A process memory layout separates code, data, BSS, heap, and stack because each region has a different purpose. Code stores instructions, data stores initialized globals and statics, BSS stores zero-initialized or uninitialized statics, heap stores dynamic allocations, and stack stores function-call data.
The central rule is to connect memory region with lifetime. Stack memory follows function-call lifetime, while heap memory follows dynamic lifetime controlled by the program or runtime.
Be the first to add a comment.