Core Architecture
Relevant source files
This document provides a comprehensive overview of the slab allocator's hybrid memory management architecture. It explains the fundamental design decisions, component interactions, and allocation strategies that enable efficient memory management in no_std environments. For detailed implementation specifics of individual components, see Heap Allocator Design and Slab Implementation.
Hybrid Allocation Strategy
The slab allocator implements a two-tier memory allocation system that optimizes for both small fixed-size allocations and large variable-size allocations. The core design principle is to route allocation requests to the most appropriate allocator based on size and alignment requirements.
Allocation Decision Tree
flowchart TD A["Layout::size() > 4096"] B["Size Check"] C["buddy_system_allocator::Heap<32>"] D["Size and Alignment Analysis"] E["size <= 64 && align <= 64"] F["Slab<64>"] G["size <= 128 && align <= 128"] H["Slab<128>"] I["size <= 256 && align <= 256"] J["Slab<256>"] K["size <= 512 && align <= 512"] L["Slab<512>"] M["size <= 1024 && align <= 1024"] N["Slab<1024>"] O["size <= 2048 && align <= 2048"] P["Slab<2048>"] Q["Slab<4096>"] A --> B B --> C B --> D D --> E E --> F E --> G G --> H G --> I I --> J I --> K K --> L K --> M M --> N M --> O O --> P O --> Q
Sources: src/lib.rs(L207 - L226)
Component Architecture
The Heap struct orchestrates multiple specialized allocators to provide a unified memory management interface:
flowchart TD
subgraph subGraph1["HeapAllocator Enum"]
J["Slab64Bytes"]
K["Slab128Bytes"]
L["Slab256Bytes"]
M["Slab512Bytes"]
N["Slab1024Bytes"]
O["Slab2048Bytes"]
P["Slab4096Bytes"]
Q["BuddyAllocator"]
end
subgraph subGraph0["Heap Struct"]
A["pub struct Heap"]
B["slab_64_bytes: Slab<64>"]
C["slab_128_bytes: Slab<128>"]
D["slab_256_bytes: Slab<256>"]
E["slab_512_bytes: Slab<512>"]
F["slab_1024_bytes: Slab<1024>"]
G["slab_2048_bytes: Slab<2048>"]
H["slab_4096_bytes: Slab<4096>"]
I["buddy_allocator: buddy_system_allocator::Heap<32>"]
end
R["layout_to_allocator()"]
A --> B
A --> C
A --> D
A --> E
A --> F
A --> G
A --> H
A --> I
J --> B
K --> C
L --> D
M --> E
N --> F
O --> G
P --> H
Q --> I
R --> J
R --> K
R --> L
R --> M
R --> N
R --> O
R --> P
R --> Q
Sources: src/lib.rs(L40 - L49) src/lib.rs(L27 - L36)
Memory Organization and Block Management
Each slab allocator manages fixed-size blocks through a linked list of free blocks. The system uses a hybrid approach where slabs can dynamically grow by requesting memory from the buddy allocator.
Slab Internal Structure
flowchart TD
subgraph subGraph1["FreeBlockList Operations"]
E["pop()"]
F["Returns available block"]
G["push()"]
H["Adds freed block to list"]
I["new()"]
J["Initializes from memory range"]
end
subgraph Slab<BLK_SIZE>["Slab"]
A["free_block_list: FreeBlockList"]
B["head: Option<&'static mut FreeBlock>"]
C["len: usize"]
D["total_blocks: usize"]
end
subgraph subGraph2["Dynamic Growth"]
K["allocate() fails"]
L["Request SET_SIZE * BLK_SIZE from buddy"]
M["grow()"]
N["Create new FreeBlockList"]
O["Transfer blocks to main list"]
end
A --> B
A --> C
A --> E
A --> G
B --> F
B --> H
E --> F
G --> H
I --> J
K --> L
L --> M
M --> N
N --> O
Sources: src/slab.rs(L4 - L7) src/slab.rs(L65 - L68)
Allocation Flow and Integration Points
The allocation process demonstrates the tight integration between slab allocators and the buddy allocator, with automatic fallback and growth mechanisms.
Allocation Process Flow
sequenceDiagram
participant Client as Client
participant Heap as Heap
participant Slab as Slab
participant FreeBlockList as FreeBlockList
participant BuddyAllocator as BuddyAllocator
Client ->> Heap: allocate(layout)
Heap ->> Heap: layout_to_allocator(layout)
alt Size <= 4096
Heap ->> Slab: allocate(layout, buddy)
Slab ->> FreeBlockList: pop()
alt Free block available
FreeBlockList -->> Slab: Some(block)
Slab -->> Heap: Ok(block.addr())
else No free blocks
Slab ->> BuddyAllocator: alloc(SET_SIZE * BLK_SIZE)
BuddyAllocator -->> Slab: Ok(ptr)
Slab ->> Slab: grow(ptr, size)
Slab ->> FreeBlockList: pop()
FreeBlockList -->> Slab: Some(block)
Slab -->> Heap: Ok(block.addr())
end
Heap -->> Client: Ok(address)
else Size > 4096
Heap ->> BuddyAllocator: alloc(layout)
BuddyAllocator -->> Heap: Ok(ptr)
Heap -->> Client: Ok(address)
end
Sources: src/lib.rs(L135 - L164) src/slab.rs(L35 - L55)
Performance Characteristics and Design Rationale
Allocation Complexity
| Allocation Type | Time Complexity | Space Overhead | Use Case |
|---|---|---|---|
| Slab (≤ 4096 bytes) | O(1) | Fixed per slab | Frequent small allocations |
| Buddy (> 4096 bytes) | O(log n) | Variable | Large or variable-size allocations |
| Slab growth | O(SET_SIZE) | Batch allocation | Slab expansion |
Key Design Constants
const SET_SIZE: usize = 64; // Blocks per growth operation
const MIN_HEAP_SIZE: usize = 0x8000; // Minimum heap size (32KB)
The SET_SIZE constant controls the granularity of slab growth operations. When a slab runs out of free blocks, it requests SET_SIZE * BLK_SIZE bytes from the buddy allocator, ensuring efficient batch allocation while minimizing buddy allocator overhead.
Sources: src/lib.rs(L24 - L25) src/slab.rs(L44 - L48)
Memory Statistics and Monitoring
The architecture provides comprehensive memory usage tracking across both allocation subsystems:
Statistics Calculation
flowchart TD
subgraph subGraph1["Per-Slab Calculation"]
I["slab.total_blocks() * BLOCK_SIZE"]
J["slab.used_blocks() * BLOCK_SIZE"]
end
subgraph subGraph0["Memory Metrics"]
A["total_bytes()"]
B["Sum of all slab capacities"]
C["buddy_allocator.stats_total_bytes()"]
D["used_bytes()"]
E["Sum of allocated slab blocks"]
F["buddy_allocator.stats_alloc_actual()"]
G["available_bytes()"]
H["total_bytes() - used_bytes()"]
end
A --> B
A --> C
B --> I
D --> E
D --> F
E --> J
G --> H
Sources: src/lib.rs(L228 - L255)
Integration with Buddy Allocator
The system maintains a symbiotic relationship with the buddy_system_allocator crate, using it both as a fallback for large allocations and as a memory provider for slab growth operations.
Buddy Allocator Usage Patterns
- Direct allocation: Requests over 4096 bytes bypass slabs entirely
- Slab growth: Slabs request memory chunks for expansion
- Memory initialization: Initial heap setup uses buddy allocator
- Memory extension: Additional memory regions added through buddy allocator
The buddy allocator is configured with a 32-level tree (Heap<32>), providing efficient allocation for a wide range of block sizes while maintaining reasonable memory overhead.
Sources: src/lib.rs(L48) src/lib.rs(L80 - L85) src/lib.rs(L158 - L163)