How MicroPython Optimizes Memory on Microcontrollers
MicroPython brings the Python 3 programming language to microcontrollers with only kilobytes of RAM by fundamentally rethinking how Python objects, code execution, and dynamic memory are managed. Standard Python (CPython) relies on large runtimes and heavy memory footprints, but MicroPython achieves efficiency through custom memory allocators, pointer tagging, string interning, bytecode execution directly from flash storage, and a specialized garbage collection mechanism designed to eliminate overhead.
Tagged Pointers and Immediate Values
In standard Python, every number or simple value is a fully allocated heap object with metadata overhead. MicroPython avoids this for common data types using a technique called pointer tagging. On 32-bit systems, memory addresses are typically aligned to 4-byte boundaries, leaving the lowest bits unused. MicroPython uses these lower bits to encode metadata directly within the pointer word itself. Small integers (typically 31-bit integers) and interned string identifiers are stored directly inside the pointer variable rather than on the heap. This allows basic math and variable passing to occur with zero dynamic memory allocation.
String Interning (QSTRs)
MicroPython handles strings through a system called QSTR (Quick String). Instead of storing multiple instances of identical strings across identifiers, dictionary keys, and code logic, MicroPython interns strings into a global lookup table. Each unique string is assigned a compact integer ID. At runtime, the engine works with these numeric IDs instead of string pointers or character arrays, saving substantial memory during dictionary lookups, attribute resolution, and function calls. Common strings are baked directly into the firmware binary in read-only memory.
Executing Bytecode from Flash (Frozen Modules)
One of the largest consumers of RAM is the compilation and storage of
code. Normally, the runtime parses Python source code, compiles it into
bytecode, and keeps that bytecode in RAM. MicroPython solves this with
"frozen bytecode." Developers use the cross-compiler tool
(mpy-cross) to compile Python source code into bytecode
beforehand. This bytecode is then compiled directly into the
microcontroller’s flash memory image (ROM). At runtime, the interpreter
reads instructions directly from flash memory without copying the code
into RAM, preserving volatile memory strictly for live state and
variables.
Micro-Optimized Bytecode Engine
The MicroPython virtual machine features a custom, highly compact instruction set. Opcode encoding is designed to keep bytecode as small as possible, often using variable-length encodings to represent arguments. Unlike CPython, which optimizes primarily for execution speed with larger internal structures, MicroPython prioritizes a minimal cache footprint and execution density so that scripts require minimal storage in both flash and RAM.
Lightweight Garbage Collection
Dynamic memory is managed by a custom mark-and-sweep garbage
collector built specifically for resource-constrained systems. It
operates on a flat memory pool divided into small allocation units
(typically 16 bytes each). The collector does not require complex
reference counting, which saves the memory overhead of maintaining
reference count fields on every object. It scans CPU registers, the C
call stack, and active Python frames to locate live objects. To mitigate
fragmentation—a major hazard on microcontrollers—MicroPython groups
smaller objects together and provides APIs like
gc.collect() and gc.threshold() to allow
developers to trigger collection proactively.
RAM Reuse with the MicroPython Buffer Protocol
MicroPython implements a memory-efficient buffer protocol that allows peripheral drivers (such as I2C, SPI, or UART) to read and write directly to pre-allocated arrays and bytearrays. By reusing the same memory buffers during input/output operations, the runtime eliminates the need to continuously allocate and deallocate temporary strings or byte buffers during continuous sensor polling or network communication.