Dynamic Memory Allocation

I am using LVGL for a project and have become confused at how it uses dynamic memory allocation. My expectation would have been that if I allocate all my objects at the beginning, my memory usage should remain consistent and LVGL should not be allocating and freeing memory while the application is running. However, that does not seem to be the case. I updated to LVGL v7.9.0 yesterday and am using the demo widgets application from lv_examples. The code runs, I can swipe from screen to screen and everything appears to work fine until randomly some time later while trying to navigate it will freeze.

#0  vApplicationMallocFailedHook () at src/main.c:227
#1  0x600769d6 in pvPortMalloc (xWantedSize=1168) at freertos_kernel/portable/MemMang/heap_4.c:253
#2  0x6005f4fe in lv_mem_alloc (size=1152) at ./lvgl/src/lv_misc/lv_mem.c:193
#3  0x600a3182 in lv_mem_realloc (data_p=0x20036844 <ucHeap+49520>, new_size=1152) at ./lvgl/src/lv_misc/lv_mem.c:321
#4  0x6005f70e in _lv_mem_buf_get (size=1152) at ./lvgl/src/lv_misc/lv_mem.c:531
#5  0x600a9f48 in draw_shadow (coords=0x20033444 <ucHeap+36208>, clip=0x2002b290 <ucHeap+3004>, dsc=0x2002b210 <ucHeap+2876>) at ./lvgl/src/lv_draw/lv_draw_rect.c:616
#6  0x600a8d84 in lv_draw_rect (coords=0x20033444 <ucHeap+36208>, clip=0x2002b290 <ucHeap+3004>, dsc=0x2002b210 <ucHeap+2876>) at ./lvgl/src/lv_draw/lv_draw_rect.c:107
#7  0x6005b832 in lv_led_design (led=0x20033434 <ucHeap+36192>, clip_area=0x2002b290 <ucHeap+3004>, mode=0 '\000') at ./lvgl/src/lv_widgets/lv_led.c:220
#8  0x60095398 in lv_refr_obj (obj=0x20033434 <ucHeap+36192>, mask_ori_p=0x2002b2c0 <ucHeap+3052>) at ./lvgl/src/lv_core/lv_refr.c:699
#9  0x6009544e in lv_refr_obj (obj=0x20033294 <ucHeap+35776>, mask_ori_p=0x2002b300 <ucHeap+3116>) at ./lvgl/src/lv_core/lv_refr.c:739
#10 0x6009544e in lv_refr_obj (obj=0x2002e6bc <ucHeap+16360>, mask_ori_p=0x2002b340 <ucHeap+3180>) at ./lvgl/src/lv_core/lv_refr.c:739
#11 0x6009544e in lv_refr_obj (obj=0x2002e60c <ucHeap+16184>, mask_ori_p=0x2002b380 <ucHeap+3244>) at ./lvgl/src/lv_core/lv_refr.c:739
#12 0x6009544e in lv_refr_obj (obj=0x2002df74 <ucHeap+14496>, mask_ori_p=0x2002b3c0 <ucHeap+3308>) at ./lvgl/src/lv_core/lv_refr.c:739
#13 0x6009544e in lv_refr_obj (obj=0x2002dea4 <ucHeap+14288>, mask_ori_p=0x2002b400 <ucHeap+3372>) at ./lvgl/src/lv_core/lv_refr.c:739
#14 0x6009544e in lv_refr_obj (obj=0x2002de0c <ucHeap+14136>, mask_ori_p=0x2002b4d0 <ucHeap+3580>) at ./lvgl/src/lv_core/lv_refr.c:739
#15 0x6004a64c in lv_refr_obj_and_children (top_p=0x2002dd84 <ucHeap+14000>, mask_p=0x2002b4d0 <ucHeap+3580>) at ./lvgl/src/lv_core/lv_refr.c:656
#16 0x6004a5a4 in lv_refr_area_part (area_p=0x2002da3e <ucHeap+13162>) at ./lvgl/src/lv_core/lv_refr.c:572
#17 0x6004a378 in lv_refr_area (area_p=0x2002da3e <ucHeap+13162>) at ./lvgl/src/lv_core/lv_refr.c:482
#18 0x6004a152 in lv_refr_areas () at ./lvgl/src/lv_core/lv_refr.c:404
#19 0x60049d52 in _lv_disp_refr_task (task=0x2002db7c <ucHeap+13480>) at ./lvgl/src/lv_core/lv_refr.c:203
#20 0x6005efba in lv_task_exec (task=0x2002db7c <ucHeap+13480>) at ./lvgl/src/lv_misc/lv_task.c:409
#21 0x6005ec38 in lv_task_handler () at ./lvgl/src/lv_misc/lv_task.c:142
#22 0x60082584 in AppTask (param=0x0) at src/main.c:56
#23 0x60076cc8 in pxPortInitialiseStack (pxTopOfStack=0x0, pxCode=0xa5a5a5a5, pvParameters=0x60082585 <AppTask+28>) at freertos_kernel/portable/GCC/ARM_CM7/r0p1/port.c:210

As you can see from the stack trace, its trying to realloc memory while updating a screen and fails. I was really not expecting this from an embedded GUI. I thought maybe it was just this new LED widget, but after some poking around it looks like everything is doing this. Am I missing something here? Typically we would want to have confidence in the maximum memory usage of the system but with this I don’t see how that is possible.

System Info
MCU: NXP RT1021
Compiler: ARM GCC
LVGL: v7.9.0

Cheers,
Chris

I would need to know the exact screen size you are testing with to investigate this on a simulator, but here is my general knowledge of how things work.

_lv_mem_buf_get is generally used to support rendering fancy effects like shadows. If you are rendering a new screen where shadows are larger/the object is bigger, there may be a single reallocation call to grow the preallocated buffer to the required size. However, LVGL should not continue allocating more and more memory with a static set of objects being redrawn. If lv_demo_widgets is doing that, there might be a bug somewhere.

Is there something else which frequently allocates/frees dynamic memory in your system?

The screen size allocated is 320x240 and we defined LCD_VIRTUAL_BUF_SIZE = (LCD_WIDTH * LCD_HEIGHT / 10). I did some memory tracing with FreeRTOS. It seems that total is memory is only allocated once every iteration of each screen has been rendered. For example, I go to the text input screen, memory is stable, I open the keyboard, and memory drops temporarily. Then if I go to the dials screen and scroll down, its the same thing. Once I have exercised everything in the UI, memory consumption appears to be stable. I had disabled the LEDs in this test, so I can’t speak for that widget specifically, but based on my high watermark, the 1152 bytes it tried to allocate could have failed.

@embeddedt
Sorry for disturb!
I also have a problem when transit from a screen to a screen several times as above description.
When transit to another screen, I did as below:
lv_obj_clean(lv_scr_act()); // delete all obj in old screen
setup_scr_new_screen(new_screen)); // create obj in new screen by lv_obj_create()
lv_scr_load(new_screen);

What does it wrong with my allocate and free?
How to solve this problem?

I can think of two things which could cause it:

  • Additional user data attached to the objects - you would have to free this manually.
  • lv_style_init should not be called multiple times on the same style - use lv_style_reset instead.

>Additional user data attached to the objects - you would have to free this manually.
Except lv_style_t, I didn’t use any additional data which allocate heap memory

lv_style_init should not be called multiple times on the same style - use lv_style_reset instead.
When screen is deleted by lv_obj_clean(screenXXX), the stytes that inited in this screen are auto-free by lv_style_reset ? or must I free them manually?

LVGL can’t free the styles because you can use them later on any object. So you need to call lv_style_reset manually. Or initialize the style once (not in setup_scr_new_screen) and just use them in setup_scr_new_screen. This way the styles will use memory all the time but you don’t need to care about freeing them.

Thanks for your support.
I solved this problem.
In lv_config.h, allocate memory size is too small for UI screen.
After fix memory size, It can be run well.

@kisvegabor @embeddedt
For a long time, but may I ask you a question?

LVGL can’t free the styles because you can use them later on any object. So you need to call lv_style_reset manually.

screen, button,… are object, and these object’s memory are free in lv_obj_clean, I think so!
Is there anything else must be free memory manually like lv_style_reset?

Additional user data attached to the objects - you would have to free this manually.

Could you give me an example about additional user data ttached to the objects?

Only styles and user data needs to be freed if required.

uint8_t * a = malloc(100);
lv_obj_set_user_data(obj, a);
...
uint8_t * b = lv_obj_get_user_data(obj)