On a board with 2 KB of RAM, building String objects in a loop can fragment memory and crash the sketch.
What the message means
Each String resizes by allocating and freeing memory. After many cycles free memory is split into pieces too small to use.
Fix it in this order
- Reserve space once with String.reserve(), or avoid String in loops.
- Use char arrays and snprintf for messages that repeat.
- Wrap fixed text in F(“…”) so it stays in flash and not in RAM.
- Print the free memory now and then to find the trend.
- Do not build big strings by adding many pieces in one go.
Why it happens
The String class allocates memory on the heap and releases it again and again. On a board with little RAM this fragments the memory, and after hours the sketch can fail.
Use fixed-size char arrays and functions such as snprintf() for text that changes, and use String only in short, simple sketches.
If you see this, do this
| You see | Do this |
|---|---|
| Fails after hours | Fragmentation. |
| Fails at start | Too much text in RAM. Use F(). |
Related
low memory, ArduinoJson memory.
Last reviewed: November 2026. These are the usual causes and the order that finds them fastest. Test on your own board; clones and cables differ.
Why this happens
An ATmega328P Uno has 2048 bytes of SRAM, shared by global variables, the stack and the heap. A String stores its characters in a heap block. Each time you append, the block may be resized, which means allocating a bigger block, copying, and freeing the old one. Do this in a loop and the heap fills with holes of many different sizes.
This is fragmentation. The total free memory may still look healthy, but no single hole is big enough for the next request, so the allocation fails and the String silently becomes empty or the sketch corrupts memory and restarts. It can take hours because each cycle leaks only a little usable space. Fixed char arrays sit at known addresses and never move, so the problem cannot occur.
How to confirm the cause
- Add a free-RAM function and print it once a second. A value that slowly falls and never recovers, or an odd drop with each message, shows a leak or fragmentation.
- Count the strings. Search for String variables, + operators on strings and String(number) calls inside loop().
- Move the String use out of loop() for one test run. If the crash time stretches from hours to days or disappears, it is the cause.
- Check the global variables report after compile; the compiler tells you how much RAM is used before the sketch even runs, which leaves less for the heap.
Worked example
A free-RAM printout next to a fixed-size message buffer, for a classic AVR board.
int freeRam() {
extern int __heap_start, *__brkval;
int v;
return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
}
void loop() {
int t = analogRead(A0);
char msg[24];
snprintf(msg, sizeof(msg), "A0=%d free=%d", t, freeRam());
Serial.println(msg);
delay(1000);
}Common mistakes
- Using snprintf with %f on an AVR board; the default AVR printf does not format floats. Use dtostrf into a char buffer instead.
- Making the buffer too small. snprintf cuts the text safely, but you lose the end of the message.
- Concatenating several pieces with + in one expression, which creates temporary Strings.
- Using strcpy or sprintf without a length, which can overflow the array.
If this did not fix it
- Check long text in Serial.print calls; wrap it in F() so it lives in flash.
- Check large global arrays, which can eat most of the 2 KB.
- Check the stack: deep recursion or big local arrays collide with the heap.
- Check for an unrelated reset cause such as a brown-out from motor current.