float var[4096]
. The quick fix was to increase the stack size of the thread calling that function from 32kB to 1MB. Another solution is to move var to static storage instead of using the stack by changing the definition to
static float var[4096]
. Upon closer inspection of the code, I saw that var was just used to copy a static array before sending it to another function. Since that function was not modifying the array, there was no reason for the copy. Removing var removed large stack allocation. Problems like this are stressful in the short term but provide an opportunity to review the concept of memory regions. Here's a concise breakdown:
Static Storage
- Memory allocated at program start, lives for entire program duration
- Size must be known at compile time
- Good for: fixed-size buffers that exist for whole program
- Example:
static uint8_t buffer[1024];
- Zero runtime allocation overhead
- Can't be resized
Stack
- Memory allocated/deallocated automatically when entering/leaving scope
- Very fast allocation/deallocation
- Limited size (often few MB)
- Good for: small-to-medium temporary buffers
- Example:
void foo() { uint8_t temp[1024]; }
- Risk of stack overflow with large allocations
Heap
- Dynamic runtime allocation
- More flexible but slower than stack
- Larger size available
- Good for: large buffers or unknown sizes
- Example:
auto* buffer = new uint8_t[1024];
- Risk of fragmentation
- Must manually manage memory
For embedded systems with fixed-size allocation needs:
1. Use static storage for long-lived, known-size buffers
2. Use stack for small temporary buffers
3. Consider static memory pools instead of raw heap allocation if you need dynamic allocation
In Visual Studio 2022, you can use Build > Run Code Analysis to detect functions with large stack allocations, they will be marked with C6262 excessive stack usage warnings.