LVGL Memory Management help

Hi there,
I started working on a Project using LVGL a few months ago and made good progress up until a few days ago when I ran into an Issue. When I power my Board up and click around in the Interface I created eventually the screen only half loads with parts of the old screen still visible. When I monitor the Board I eventually get an Error that my Task watchdog got triggered. Now If I remember right I read a Thread here somewhere (Unfortunately can’t find it at the moment) that this can happen when LVGL runs out of Memory is that correct?
I am using one of those Sunton AliExpress Displays (ESP32-2432S028) and the ESP-IDF 5.1.2 Framework to write my Code. The Codebase is entirely in C++ and I use Classes as wrappers for my Window, got a Parent class with a virtual function for building the UI and all other windows are subclasses of the main class. When the Display boots It creates a few Windows that are most frequently viewed and the Idea was to have the others be created on Demand, and when the User is done they get deleted. Now this already caused some Issues for me with a Load Screen I created as it got repeatedly created and Destroyed, it eventually led to the same Issue described above. At the time I assumed it was an issue with my Task but by coincidence, I changed it so that the screen only gets created once and the Issue went away along with other changes to the Task.

Now I guess my Question is, is there a safe way to delete screens and create a new one of the same type again at the moment I am using the cpp new and delete to create instances of my Windows classes. But even deleting them does not seem to clear LVGL’s Memory even though none of the LVGL objects in the class are dynamically allocated.
Anyone can help me out with this? Whould be highly appriciated :slight_smile:

Your issue is memory fragmentation. What is happening is there are things being created and destroyed at various times and when the memory allocation occurs for a bunch of different items things get put where they will fit. and when they get garbage collected what was once a contigious chunk of memory may no longer be that. This fragmentation is what is causing your issue.

All of the widgets are an lv_obj_t type. so you can allocate an array that will hold a bunch of objects and you can add and remove them from the array as needed when they are being created and destroyed. This will keep the memory allocated at all times so you don’t end up with a lot of memory use from keeping the objects around and just hiding them and you will not get the fragmentation from creating and deleting them all the time. Make the array the size of the screen that has the most number of widgets that you are going to delete.

lv_obj_t *obj_arry heap_caps_malloc(sizeof(lv_obj_t) * array_size, HEAP_CAPS_SPIRAM);

widgets can have their parents changed. Buttons are a very common item and you can share them by simply changing the parent. You might have to change the position and any styling/labels once it has been set to a new parent. so instead of deleting it you can reuse it on more than a single screen.

Thanks a lot for the quick response and the detailed explanation :slight_smile:
It’s a fantastic idea, will try to implement it tomorrow :smile:

No worries m8, gotta think outside the box. I am from the days of 640K of memory and needing to fit everything in that amount of space. This is for a PC and when buying 1 MB of RAM had a price of about 200 bucks. That is almost 600 dollars in today’s money. That 1mb of ram is like having 1/2 a terabyte these days.

People that write programs don’t have the same coding ethics as they did back then. they don’t write the most efficient code that can be written. They don’t fix memory leaks (Chrome/Firefox). This is because of the ample resources a PC has. One of the things that gets missed as a result of them not taking the time to reduce memory use is fixing poor performance areas. I never could wrap my head around how a website once loaded ends up taking up over a GB or ram when it only sent 50 mb of images and there is nothing that would justify it using that much memory.

It is good to have these kinds of issues because you learn how to write software in the most efficient manner possible. I think that MCU’s are what every new developer should be taught to use. Writing a program that will fit within a hard limit of resources and have it run properly.

If you have any other questions or maybe you just want some ideas, please feel free to ask. If I have the answer I will give it. Even if I don’t have the answer I can BS my way through it to make it sound good enough to be an answer. LOL… I won’t do that but I will see if I am able to locate the answer for ya

1 Like

Hi, I need some more help, and since you offered here I am :smiley:
So today I had a chance to sit down and work on an implementation of your Idea but it got me thinking. Most functions from LVGL that create widgets return a Pointer to the newly created object, and we just created an Array of lv_obj_t which of course makes sense if we want to manage these objects ourselves. But how would I create a widget that we can store in the array?
Imidietly to mind comes something like this:

LVGL_Window::lv_memory_buffer = (lv_obj_t*)heap_caps_malloc(sizeof(lv_obj_t) * LV_MEMORY_BUFFER_SIZE, MALLOC_CAP_SPIRAM);
if (LVGL_Window::lv_memory_buffer == NULL)
{
    ESP_LOGE("LVGL_Window", "Failed to allocate memory for lv_memory_buffer");
}

LVGL_Window::lv_memory_buffer[0] = *(lv_obj_create(NULL));

but dereferencing it like that would that not store a copy of the object and not the actual object?
I do apologize if this question seems silly, I am still learning. :upside_down_face:
What I want to know is how I can create new objects and store them in the buffer.

That’s kinda what drew me into Embedded systems, working within such limited environments is a really fun and complex topic :slight_smile: And while I can’t claim to have those coding ethics you speak of just yet I do get what you mean. Truly boggles the mind how my Browser can take up multiple GBs of my Ram with just a few Tabs open.

If you are using ESP32.
As described in ESP-IDF programming Guide:

“However, due to a technical limitation, the maximum statically allocated DRAM usage is 160 KB. The remaining 160 KB (for a total of 320 KB of DRAM) can only be allocated at runtime as heap.”
link to doc

So first 160 KB is for the application static variables.
Second 160 KB for a heap (variables allocated at run-time, like the widgets).

But if in lv_config.h
#define LV_MEM_CUSTOM 0
then memory for widgets allocates from first 160 KB of size
#define LV_MEM_SIZE (64U * 1024U)
In this case not enough memory to store widgets constantly in RAM.

Set LV_MEM_CUSTOM 1
In this case your widgets will place in second 160 KB in heap.
You will have enough memory for the constant storage of widgets in the memory. And it will not be necessary to create and remove them dynamically.

I use VS Code + Platformio.
Platformio-Inspect to see the memory usage.

That’s what Java does. Anything Java including JavaScript. It’s a resource hog that doesn’t like to let go of unused resources. There are also tons of memory leaks in the code written by website developers and they have no interest in fixing them. That is what client side processing has given us.

As far as the memory management aspect of it. It all depends on how your application is written. in Version 9 of LVGL you have the ability to implement your own form of a GC and that would probably be the best thing to do possibly.

I have actually been thinking about this a bit. It dawned on me that LVGL will always allocate new memory for objects that get created. So what I told you to do is not going to work. maybe @kisvegabor can chime in with a possible way to go about this.

LVGL as it is written is going to free the memory when a widget gets removed and then allocate new memory when a new widget gets added. I am thinking that is may not be the best thing to do and that the memory should be reused in place. Or at least give the user the option of being able to do that by supplying the obejct that is needing to be replaced.

The only way I can see you being able to stop the fragmentation is you are going to have to roll your own memory management and garbage collection. I know in LVGL 9 you can do this. I do not know if it can be done in version 8.x.

In version 9 in the lv_conf you can set the following macros that will allow you to control the memory allocation.

LV_USE_STDLIB_MALLOC
LV_MEM_POOL_INCLUDE
LV_MEM_POOL_ALLOC
LV_ENABLE_GLOBAL_CUSTOM
LV_GLOBAL_CUSTOM_INCLUDE
LV_GLOBAL_CUSTOM
LV_GC_INIT

It would be nice if there was some builtin functionality in LVGL that manages memory fragmentation or at least allows the user to be able to create new objects by reallocating space used by an object that is wanting to be removed. adding a function like lv_obj_replace_child where a user could pass a function pointer to one of the create functions along with a pointer to the object that the user wants to replace with perhaps an additional parameter to specify that the size of the objects must be an exact match or if it is OK that the new object be smaller. A larger object than could fit into the space would return a NULL if successful the the object that gets created would be returned.

That would be useful in helping to manage fragmentation.

I think at the present time the best thing you can do is reusing widgets and reparenting them if needs be.

Keep in mind that lv_obj_t is not just a GUI element. It can also be used as a container to hold widgets. This is done by setting opacity of all of the colors to zero and setting the outline and border widths to zero and set the size to match what the parents size is. Anything you want to bulk hide/show or bulk reparent can be done all at once, you only need to show or hide the container and it will propagate to all of the widgets in the container. by setting the parent of the container the children will move with the container so effectively moving to the new parent.

so ignore what I had stated previously because there is no easy way to go about reallocating space used by the widgets (to my knowledge). I will bring that up to @kisvegabor and see what he thinks about the idea. He might be able to shed a little light on how to do it if it can already be done.

If you look at this video.

you will see there are tabs at the bottom of the display. inside those tabs are controls that are grouped together. when I change the tab so it is no longer viewed if the same kind of a control is on the tab that is now in view I reparent a group of controls to that tab, change the labels and values. I could go one step further and watch the positioning when scrolling so when a group of controls goes off screen I could unparent it and hide it until the same kind of control is about to come into view and then set the parent and position and change the label and values as needed. In that example it could be written so there are only 4 sets of the slider/button grouped controls that would be needed and they would get moved around as needed. They never get deleted only hidden from view if they are not in use. the total number of the slider/button coups that would be needed if I created them all at the same time would be 12. The most that are in use on a single tab is 6 and that is the number of them that I am using and I could reduce that to 4 if needed.

This is almost the final version of that project

If memory serves in that video there are a very large number of objects that are created because of doing the containers for things. Like the arced text around the gauges. each letter is in a container and the containers for the letters that make up a word are in a container. and that container is in another container that holds all of the arced labels.

Ok so after some further thinking, testing, and looking at how LVGL allocates memory I came to the conclusion that It was my fault after all :melting_face:
So for anyone facing similar issues make sure to use the following:

lv_obj_del(screen_object);

building that into my Screen class destructor did solve the initial problem. I am able to create and destroy a screen object with around 70 widgets repeatedly without Issues.
Still possible that I will eventually run into fragmentation Issues but for now it seems to be working. :pray:
I will update this thread should this have only been a temporary solution.

Thanks to everyone who responded to help out, very much appreciated :slight_smile:

so you weren’t deleting the objects?

Yeah… I thought since I only allocated on the stack I did not have to. Only when I checked how lvgl creates its objects did it occur to me to run the delete function. :sweat_smile:

That’s my fault because I made an assumption that you were deleting them. LVGL actually allocates the memory out of the stack when creating objects. this is done so the user doesn’t have to keep reference to objects. So your issue really was not fragmentation it was running out of memory because the memory was not being freed when an object was no longer being used.

smacks forehead