🧠 Memory Management in FluxSand
This document describes the memory usage strategy and how safe, efficient memory management was implemented throughout the FluxSand project. The focus is on avoiding leaks, minimizing overhead, and adhering to best practices in modern C++ for embedded systems.
🔒 Encapsulation and RAII Principles
All memory in the system is managed using stack allocation or Resource Acquisition Is Initialization (RAII) style wrappers.
- No raw
new
/delete
used in application code - All class members use value types or smart references
- Ownership is clear and confined to the constructors and destructors
Example:
class Gpio {
// Chip and line pointers are acquired once, released in ~Gpio
gpiod_chip* chip_;
gpiod_line* line_;
};
🧼 Ensures all resources (file descriptors, hardware handles) are cleaned up even if an exception occurs.
🧵 Thread-Safe Resource Lifetimes
Each thread in FluxSand is responsible for its own lifecycle and cleanup.
- Threads such as
InferenceTask
,ThreadTask
, andCalibrateThreadTask
are joined on destruction - No dangling references or race conditions due to premature deallocation
std::binary_semaphore
and message-passing used instead of shared raw pointers
📊 Memory Footprint Control
FluxSand is designed to run on Raspberry Pi 5 with limited memory. Strategies to minimize memory use:
- No dynamic STL containers with uncontrolled growth
- Bounded history buffers (
std::deque<float>
with fixed size) std::array
used overstd::vector
where applicable- ONNX model preloaded into memory only once and reused
Example:
std::deque<ModelOutput> prediction_history_; // bounded in size
🚫 Common Pitfalls Avoided
Anti-Pattern | What We Used Instead |
---|---|
Manual new / delete | Stack objects and RAII |
Global mutable state | Private members with clear ownership |
Leaky loops or buffers | Fixed-size containers, bounds checking |
Unsafe memory reuse | Stateless callbacks and message passing |
✅ Summary
FluxSand achieves memory safety and efficiency by:
- Using RAII and encapsulated class ownership
- Ensuring thread lifecycles match resource scope
- Avoiding leaks, dangling references, or over-allocation
- Using predictable data structures with bounded memory usage
This results in a robust system that runs reliably on embedded hardware without the risk of fragmentation or unpredictable memory growth.
For profiling results or heap usage statistics, see the “Assessment” section.