Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

During execution of a program, memory is used to store code, constants and data as well as manage function calls. A process’ memory is divided into several segments (see Figure 1):

Process memory layout

Figure 1:Process memory

7.1.1Text (Code)

In the Text segment is stored the whole machine code generated by the compiler. This memory zone is set as read-only by the Operating System: trying to write in this zone would trigger a segmentation fault.

7.1.2.rodata

The .rodata segment is where are stored constants. As for the Text segment, this memory zone is set as read-only by the Operating System: trying to write in this zone would trigger a segmentation fault.

Declared constants are usually stored inside the .rodata segment, though they may be inlined by the compiler for optimization:

3.14159

Literal strings are stored inside the .rodata segment:

"Hello, world!"

7.1.3.data

The .data segment stores initialized static variables: static variables that have initialization values at compile time.

Example with an integer:

92340

Example with a structure initialized using a function:

Point { x: 0, y: 0 }

7.1.4.bss

The .bss segment (Block Started by Symbol) is where are stored uninitialized static variables: static variables that have no known initialization values at compile time. However, the compiler, knowing the sizes of those objects, allocates the needed size inside the generated program’s file. At runtime, the Operating System provisions the necessary memory zone, and initialize it to zeros. Later on, during program’s execution, these data will eventually receive values.

In Rust, zero initialized variables are stored in the .bss segment:

N=10
BUFFER=[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0]

7.1.5Stack

The stack is a region of memory that stores local variables created by each function (including the main function). It operates in a last-in, first-out (LIFO) manner, meaning that the most recently added function call is the first one to be removed when it completes.

Each time a function is called, a new stack frame is created on top of the stack to hold (see Figure 2):

When the function returns, its stack frame is popped off the stack, and execution is resumed at the return address.

In the following example, functions foo_a(), foo_b() and foo_c() are pushed on the stack, as well as their variables/parameters n, m and o:

58
Memory stack

Figure 2:Memory stack

7.1.5.1Stack overflow

If a program uses too much stack space, through too many nested function calls and/or heavy usage of local variables, it can lead to a stack overflow error, causing the program to crash, as show in Figure 3.

Stack overflow

Figure 3:Stack overflow

In the following example we push a huge array on the stack that would generate an overflow at runtime (not executed here to avoid crashing the kernel):

let array = [123; 1000_000_000];
array.len()

7.1.5.2Call stack

The call stack is a conceptual representation of the active stack frames at any given point during program execution. It shows the sequence of function calls that led to the current point in the program, with the most recent call at the top (see Figure 4).

Call stack

Figure 4:Call stack

7.1.6Heap

The heap (see Figure 5) is a region of memory used for dynamic memory allocation, where objects are stored and managed during the program’s execution. Unlike the stack, which has a strict LIFO structure, the heap allows for flexible memory allocation and deallocation. Objects in the heap can be created and destroyed in any order, and their lifetimes are not directly tied to function calls.

Memory heap

Figure 5:Memory heap

The memory heap grows according to the needs of the program. Each time more memory is needed, the program sends a request to the operating system. The theoretical maximum size of the heap is determined by the operating system, architedture (32-bit vs 64-bit) and configuration, but is always larger than the available physical memory, thanks to virtual memory techniques. This is however aperformance bottleneck, as accessing data in RAM is much faster than accessing data in virtual memory (on disk). Therefore, it is important to manage memory usage efficiently to avoid excessive swapping between RAM and disk.

The following example makes usage of heap objects:

["Le Petit Prince", "Le avventure di Pinocchio. Storia di un burattino"]