Memory allocation failed, but I have plenty of memory free! (memory fragmentation)

Sometimes, you can see with gc.mem_free() that you have plenty of memory available, but you still get a message “Memory allocation failed”. As described in a previous section, the memory manager and garbage collector work to clean up and identify available memory space.  After running your program for a bit, your memory may become “fragmented”, meaning that there are only small contiguous areas of open memory space. While the total memory available may be sufficient for your new object, if there is no continuous memory space available the error is raised “Memory allocation failed”. (Note: Doing a “defragmentation” of memory is not feasible with the memory structure of CircuitPython, but there are things you can do to help reduce memory fragmentation.) Here are several strategies to reduce memory fragmentation to help retain space for allocating larger memory objects.

Call the garbage collector early and often

The garbage collector is triggered when the memory manager cannot find an free memory space to fit a new variable. But you can call the garbage collector into action in your own code to clear out unused variables and reduce memory fragmentation.

Call gc.collect() periodically in your code to free up the memory space from unused memory objects, especially in code locations immediately after memory items are dereferenced. If you are done with a variable, and use del large_variable, call the garbage collector immediately.

After function returns

Using functions is a good way to “encapsulate” and free up your memory usage. After a function call returns from execution, all the local variables are immediately dereferenced and become “phantom” objects.  After a function call that creates a lot of large variables, call gc.collect() immediately to free up all the “phantom” memory objects that were dereferenced when the function returned. Each call to gc.collect() will take some processor time but it can significantly reduce your memory fragmentation.

If you are reaching the limits of your memory, sprinkle in gc.collect() in your code to help reduce memory fragmentation. After a function returns is always a great time to call gc.collect() to free up space.

Before creating large memory objects

Another good time to call gc.collect() is immediately prior to creating large objects. There could be “unused” phantom variables clogging up a space that could accommodate your new big variable. By running the garbage collector, you clear out those unused variables so the new memory allocation can be placed in that space.

Other techniques to reduce memory fragmentation:

  • Use functions where it makes sense for memory items that are temporary. Take advantage of the fact that after a function closes, all its local variables are automatically “unused”. Call gc.collect() after function returns to reclaim all that memory space as free again.
  • Use gc.collect() prior to any large memory allocations. This will help reduce the fragmentation since you clear out all phantom unused variables before you request a new chunk.
  • For bitmaps and text labels that are static through your program, create them early in the life cycle ofyour code. If you allocate them toward the end, it is a higher chance that your memory will get fragmented and cause a memory allocation error when you later create the bitmap or text label. Allocate these large items early while memory space is relatively wide open.
  • Changing text in a label causes reallocation of the label’s memory. Consider ways to avoid changing the text in your label.
  • Special note with text labels: bitmap_label.label uses less overall RAM but it needs all its memory in one chunk. Somewhat counterintuitively, label.label uses more total RAM but splits it up into smaller pieces, so consider using label if you need to allocate text labels with frequently changing text. This is one way to “walk between the raindrops” when your memory is fragmented. If you really need to redo the text in a label frequently, evaluate whether label.label can help.
  • Advanced programmers: Allocate a large memory buffer early in the life of your code and reuse the same memory buffer through your program.

This guide was first published on Apr 17, 2021. It was last updated on Apr 17, 2021.

This page (Memory: Reducing fragmentation) was last updated on May 16, 2021.

Text editor powered by tinymce.