lvgl / lv_binding_micropython

LVGL binding for MicroPython
MIT License
237 stars 156 forks source link

ESP32 Memory Drain #185

Closed jdtsmith closed 2 years ago

jdtsmith commented 2 years ago

I reported over on the prototype ST7789 driver page a memory issue I've experienced with LV_MP. I now think it isn't specific to that driver. The issues is a constant and quite uniform free memory drain of 808bytes/s, which occurs as soon as the display is enabled, without any LVGL elements of any kind created or showing. It drains free memory all the way down to ~0bytes, before being collected. I believe this could be leading to some out of memory errors I'm getting with MP-LVGL, if GC doesn't happen when needed.

@russhughes conjectures this might be some MP-specific dynamic memory being allocated and freed repeatedly in LVGL's event loop. Is this a possibility, or perhaps even expected? Is this drain problematic in your opinion?

Thanks for LV_MP.

amirgon commented 2 years ago

Are you are talking about gc memory? (lv_micropython also uses non-gc RAM for DMA for example, which should be explicitly freed)

If so, you should always measure it right after running gc.collect().

If Micropython tries to allocate gc memory and there isn't enough, it would automatically initiate gc.collect() and fail only if there isn't enough memory after the gc collection.

Are you seeing this behavior after running gc.collect()?

jdtsmith commented 2 years ago

Thanks. Yes, GC memory. I find this with:

import gc,utime
gc.collect()
for i in range(20):
    print(f'{i*8:4} - {gc.mem_free()}')
    utime.sleep(8)

leading to:

   0 - 99312
   8 - 92848
  16 - 86384
  24 - 79920
  32 - 73456
...

i.e. 808bytes/second. I note that free memory does not drop at all like this until an LVGL display is loaded, and then starts dropping immediately afterwards.

So is this expected, maybe due to a continuous allocation/freeing in the event loop? Just very surprising. But if true, possibly not responsible for the memory issues I've experienced, and I'll have to look elsewhere. Is there a way to query LV-MP for its current allocated GC memory usage?

amirgon commented 2 years ago

So is this expected, maybe due to a continuous allocation/freeing in the event loop?

It's not very surprising. Every Micropython object that is created consumes RAM. Memory is only freed when gc is collected. This is just how Garbage Collection works. If you add gc.collect() in your loop you would see that gc RAM is stable.

Is there a way to query LV-MP for its current allocated GC memory usage?

LVGL itself uses Micropython's memory allocation functions for allocating and deallocating memory.
This is defined here: https://github.com/lvgl/lv_binding_micropython/blob/af7522d7314fc785a72f7de0640716be239e8b37/lv_conf.h#L64-L66

You could define your own alloc/free wrapper functions that log memory allocations before calling m_malloc/m_free in order to see if these allocations come from LVGL.

The event loop itself is implemented in Python so these allocations could come from there too, see lv_utils.py. Memory allocation could also come from Micropython itself when it handles the machine.Timer callbacks in the event loop, for example.

jdtsmith commented 2 years ago

I noticed differences using the asyncio event loop instead, so perhaps it's Timer related.

amirgon commented 2 years ago

Since the gc behavior you described is expected, I'm closing this now. Please feel free to reopen if you come to the conclusion that there is a bug here.