FYI: Memory allocation changes for ESP32 #12316
Replies: 3 comments 8 replies
-
Typo on 4th summary point, ESP32S3 is mentioned twice in succession. Should one be EPS32C3? |
Beta Was this translation helpful? Give feedback.
-
@jimmo , previously we could programmatically fetch the amount of free memory available by calling |
Beta Was this translation helpful? Give feedback.
-
There is another proposed tweak to this scheme, designed to not prematurely exhaust the system heap. See the PR, here: #13035 |
Beta Was this translation helpful? Give feedback.
-
Summary:
gc.mem_free()
on ESP32 reports approximately how much total memory is available for MicroPython, but the real total may be higher or (rarely) lower.For most applications, there should be no changes required, other than hopefully the issues around using SSL should be improved. However one consequence is that you can no longer fully rely on
gc.mem_free()
to tell you how much more allocation can be done. When MicroPython runs out of memory, it will now request more RAM from the OS. The result ofgc.mem_free()
is the amount of free memory already assigned for MicroPython, plus the largest free block of memory that the OS has available to provide to MicroPython on demand. There will often be additional free blocks of OS memory that could be added in addition. If MicroPython keeps allocating memory then this "grow on demand" process continues until the OS is unable to provide any more memory.You can use
micropython.mem_info()
to find out what specifically what the split is between free memory already allocated to MicroPython, and the largest free allocation that could be allocated to MicroPython on demand. This is a lower bound on how much more memory will be available.(Thanks to @projectgus for all the work on this. Despite this small oddity with the reporting of the memory, overall this is a very important change for the ESP32 and should address a lot of problems we've seen reported).
Background:
On the ESP32 port, MicroPython runs as an application within the RTOS provided by the Espressif IDF. This is different to, e.g. the stm32 or rp2040 ports where we run "bare-metal" and MicroPython has complete control over everything. One long-standing issue is that MicroPython has to ask for memory from the RTOS, and this leads to two problems:
A further problem is on ESP32 boards with external PSRAM is that MicroPython has historically allocated a large chunk of this RAM (until recently, we allocated the whole amount, and more recently just half of it, to leave some for the OS). However, this means that when the garbage collector runs, even if your application only has a small working set, it has to do a lot of work to scan the entire RAM during the collection process. (In more detail, unless gc.threshold is configured, the GC only runs when it runs out of RAM, so it will fill up the entire allocation even if there are very few "live" objects, before doing a huge collection to free everything).
On non-PSRAM boards, we were limited to the size of the largest contiguous region (which varied significantly between IDF releases), but also there was a good chance that we left very little to the OS (this is where the SSL memory errors come from).
Now MicroPython tries to start by allocating a small region for the heap, and then only growing as necessary. The initial heap is only 64KiB but will grow on demand.
Details of what's changed:
gc.mem_free()
returns how much RAM is free in all heap splits, plus the largest free block of RTOS memory that could be made into a new "split" on request. This is a reasonable heuristic for "total free memory".micropython.mem_info()
prints separate values for how much memory is free in the existing MicroPython memory heap, and what size a "max new split" could be if added on demand. There might be still more available after that, in the next contiguous block.Here is an example of a
UM_TINYPICO
board (which has 4MiB of external PSRAM).At boot, it has allocated a 64kiB split, and used 4976 bytes. The total free is reported as 4122064 bytes, which is the total of 58656 bytes free in the existing split plus 4063232 bytes available in the PSRAM to be automatically added as a new "split" on demand.
Here is an example on a generic ESP32 module (without PSRAM).
Allocating 100KiB has automatically expanded the total memory used by MicroPython from 64000 bytes to 166528. Additional memory is still available in the RTOS to add a new "split" with more memory, if requested.
Other notes:
SSLContext
class.Please let us know if you have questions or ideas.
(EDIT 2023-09-19: Updated for the newer behaviour of
gc.mem_free()
, merged after the initial change.)Beta Was this translation helpful? Give feedback.
All reactions