ESP32 Flash/PSRAM & Memory Leaks: Understanding the Memory Map and Preventing Crashes
Jan 21th,2025

Flash vs Internal RAM vs PSRAM
Why Flash ≠ RAM
ESP32 Flash, along with internal RAM and PSRAM, represents one of the three distinct types of memory in the system. The ESP32 uses three very different types of memory, and treating them as interchangeable is a fast path to instability.
√Flash memory stores your firmware, static data, and filesystems. It is non-volatile and comparatively slow. While parts of Flash can be memory-mapped, it is not general-purpose RAM. You cannot safely treat Flash as a pool for dynamic runtime allocations.
√Internal RAM (SRAM) is where your program truly lives at runtime. It is fast, limited, and shared between your application, the Wi-Fi/BT stacks, FreeRTOS, and drivers. When this memory becomes fragmented or exhausted, crashes follow.
√PSRAM is external pseudo-static RAM. It expands available memory significantly, but it is not “faster RAM” or a free upgrade. It has higher latency and more constraints than internal RAM.
A large percentage of ESP32 crashes come from using the wrong memory for the wrong purpose. Flash is not heap, PSRAM is not a drop-in replacement for internal RAM, and assuming otherwise leads to reboots that seem random but are entirely predictable— see the ESP32 Pinout Guide for how GPIOs and memory layout affect hardware behavior.
Official documentation about PSRAM by Espressif: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/external-ram.html#esp32-rev-v1

The ESP32 Memory Map
Address Space vs Usable Memory
The ESP32 memory map often misleads developers into confusing address space with usable memory. Another common trap is confusing address space with usable memory. The ESP32 may expose a large address range, but only a portion of it is suitable for dynamic allocation.
Internally, RAM is divided into IRAM and DRAM:
• IRAM (Instruction RAM) holds time-critical code. Interrupt handlers and performance-sensitive routines often need to live here.
• DRAM (Data RAM) stores global variables, stacks, and most heap allocations.
Not all internal RAM is available to malloc. Some is reserved for the system, some for networking, and some for task stacks. This is why malloc() can fail even when tools report “free memory.”
Heap fragmentation makes this worse. You may have enough total free bytes, but no contiguous block large enough to satisfy a request— this kind of behavior also shows up in real projects like the ESP32-CAM Setup and Troubleshooting Tutorial where memory constraints impact stability. From the application’s point of view, memory “exists,” but allocation still fails.
Flash and Partitioning Basics
Silent Project Killers
Partition misconfiguration is one of the most overlooked causes of ESP32 instability.
The Flash is divided into partitions: application code, OTA slots, NVS, and filesystems like SPIFFS or LittleFS, forming the core of the ESP32 Flash partition structure.If these are incorrectly sized, problems appear far from their source.
Common pitfalls include:
• APP partition too small: the firmware builds but crashes or behaves unpredictably.
• OTA enabled unnecessarily: half the Flash is reserved but never used.
• Filesystem too large: starving the application of Flash space.
• Flash size mismatch: the IDE is set to 4 MB, but the chip has 8 MB (or vice versa).

These issues rarely produce clear errors. Instead, you see random resets, corrupted data, or failures after updates. Always confirm that the configured Flash size and partition scheme match the actual hardware— for guidance on how to properly configure these partitions and avoid such crashes, see this ESP32 Custom Partition Tables Tutorial.
PSRAM Enablement in the Arduino IDE
Enabled ≠ Used
ESP32 PSRAM usage in Arduino often confuses beginners. Enabling PSRAM in the Arduino IDE does not mean your application is using

By default, malloc() and new still allocate from internal RAM. PSRAM is only used if:
• The allocation is explicitly directed there, or
• Internal RAM is exhausted and the allocator is configured to fall back.
[D][esp32-hal-psram.c:47] psramInit(): PSRAM enabled
[D][PSRAMTestArduino.ino:4] setup(): Total heap: 393356
[D][PSRAMTestArduino.ino:5] setup(): Free heap: 367948
[D][PSRAMTestArduino.ino:6] setup(): Total PSRAM: 4194252
[D][PSRAMTestArduino.ino:7] setup(): Free PSRAM: 4194252
This leads to a common scenario:
“PSRAM is detected, but my program still runs out of memory.”
Typical causes include:
• Large buffers allocated with standard malloc
• Camera or image processing code assuming PSRAM is automatic
• Network libraries that only use internal RAM
Unless you consciously design for PSRAM usage, it often sits idle while internal RAM is exhausted.
The Real Causes of “Memory Leaks” on ESP32
ESP32 memory leak issues are often misunderstood. True memory leaks—where memory is allocated and never freed—do exist, but they are less common than developers think. Most ESP32 “leaks” are actually fragmentation.
Frequent offenders include:
• Repeated creation and destruction of String objects
• JSON documents that grow and shrink dynamically
• Network clients that allocate buffers on each request
• Reconnect loops that recreate objects without full cleanup
Over time, these patterns break the heap into small pieces. Free memory decreases slowly, or the largest free block shrinks until one allocation fails and the system crashes— a pattern that also appears in real ESP32 projects like the ESP32-CAM Face Recognition: Building a Reliable End-to-End Pipeline, which highlights how careful memory usage is needed for stable operation. From the outside, it looks like a leak. Internally, it is a death by fragmentation.
From the outside, it looks like a leak. Internally, it is a death by fragmentation.
Symptoms and Quick Triage
Understanding the failure pattern helps you narrow the cause quickly.
• Gradual heap decline: usually fragmentation or repeated allocations in loops.
• Sudden failure during a feature: often a large allocation (TLS, JSON, image buffer).
• Random reboots: watchdog resets, stack overflow, or heap corruption.
• Feature-specific crashes: camera, HTTPS, or OTA code stressing memory limits.
If a reboot always happens during Wi-Fi reconnects or HTTPS requests, memory pressure is almost certainly involved, which is a typical ESP32 crash
An Arduino-Friendly Leak-Hunting Workflow
You do not need deep ESP-IDF tooling to debug most memory issues.
Start simple:
• Log ESP.getFreeHeap() at key points to monitor ESP32 heap usage over time.
• Compare free heap before and after major operations.
• Track the minimum free heap, not just the current value.
Add structure to your checks:
• Log memory at boot
• After Wi-Fi connection
After the first request
• After repeated cycles of the same operation
If free heap never returns to its original level, you have either fragmentation or a leak.
Avoid allocating memory inside tight loops. Prefer static buffers, reuse objects, and keep lifetimes explicit— these practices help reduce heap fragmentation and improve reliability in resource-constrained systems, as explained in this Optimizing RAM Usage in Embedded Systems guide. The goal is a steady-state heap that does not shrink over time.
PSRAM Allocation Strategy
Avoiding Self-Inflicted Pain
PSRAM allocation on ESP32 requires deliberate planning. PSRAM is powerful when used correctly, and destabilizing when misused.
Good candidates for PSRAM:
• Large buffers
• Image frames
• Audio or video data
• Large JSON documents
• File read/write buffers
Keep in internal RAM:
• Network-critical data
• Small, frequently accessed structures
• Timing-sensitive code paths
• Control logic and state machines
Blindly moving everything to PSRAM can increase latency and expose driver limitations. Some peripherals and DMA operations still expect internal RAM. Stability improves when PSRAM is used deliberately, not aggressively.
Conclusion
ESP32 memory issues are rarely random. They are the result of misunderstanding how Flash, internal RAM, and PSRAM actually work together— see the ESP32 Memory Types Overview for details on how each memory type is used and what mistakes to avoid.Flash is for storage, internal RAM is scarce and critical, and PSRAM must be used deliberately—not blindly. Most so-called “memory leaks” are caused by fragmentation, repeated allocations, or poor object lifetimes rather than true leaks. Stable ESP32 systems come from predictable memory usage: fixed buffers, explicit lifetimes, careful partitioning, and conscious PSRAM placement. When memory behavior becomes deterministic, crashes stop being mysterious—and systems start running reliably for weeks or months, thanks to better ESP32 memory management.
