在lvgl中,当两个页面之间互相切换时,先将第一个页面的控件全部删除,在创建新页面的控件,但是有时执行后会发现会出现内存泄漏:两个页面来回切换,内存占用不断提高,直到卡死。

1、情况复现

使用最简单的方式复现问题,模拟器新建两个页面,每个页面在创建时新建一个style对控件进行修饰,不断切换页面后,内存占用会不断升高
现象截图:
leak.gif

测试代码:

static lv_style_t style ;
static lv_obj_t * page1_obj ;
static lv_obj_t * page2_obj ;
static uint8_t page = 0 ;
void log_get_lvgl_mem(void)
{
    lv_mem_monitor_t mon;
    lv_mem_monitor(&mon);
    uint32_t used_size = mon.total_size - mon.free_size;
    uint32_t used_kb = used_size / 1024;
    uint32_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102;
    printf("%"LV_PRIu32 ".%"LV_PRIu32 " kB used (%d %%)\n"
                            "%d%% frag.",
                            used_kb, used_kb_tenth, mon.used_pct,
                            mon.frag_pct);
}
static void event_handler(lv_event_t * e)
{
    lv_obj_t * obj = lv_event_get_target(e);
    lv_event_code_t code = lv_event_get_code(e);
    if(LV_EVENT_CLICKED == code)
    {
        // lv_style_reset(&style);
        if(0 == page)
        {
            page = 1 ;
            lv_obj_clean(lv_scr_act());
            test_creaty_page_2();
            log_get_lvgl_mem();
            printf("\r\nchange to page2\r\n");
        }
        else if(1 == page)
        {
            page = 0 ;
            lv_obj_clean(lv_scr_act());
            test_creaty_page_1();
            log_get_lvgl_mem();
            printf("\r\nchange to page1\r\n");
        }
        else
        {
            printf("error!\r\n");
        }
    }
}

void test_creaty_page_1(void)
{
    lv_style_init(&style);
    lv_style_reset(&style);
    lv_style_set_radius(&style, 10);
    lv_style_set_border_side(&style, LV_BORDER_SIDE_NONE);
    lv_style_set_bg_opa(&style, LV_OPA_COVER);
    lv_style_set_bg_color(&style, lv_color_make(255, 0, 0));
    page1_obj= lv_obj_create(lv_scr_act());
    lv_obj_add_style(page1_obj, &style, 0);
    lv_obj_set_pos(page1_obj, 0, 0);
    lv_obj_set_size(page1_obj, 200, 200);
    lv_obj_add_event_cb(page1_obj, event_handler, LV_EVENT_CLICKED, NULL);
}
void test_creaty_page_2(void)
{
    lv_style_init(&style);
    lv_style_reset(&style);
    lv_style_set_radius(&style, 10);
    lv_style_set_border_side(&style, LV_BORDER_SIDE_NONE);
    lv_style_set_bg_opa(&style, LV_OPA_COVER);
    lv_style_set_bg_color(&style, lv_color_make(0, 255, 0));
    page2_obj= lv_obj_create(lv_scr_act());
    lv_obj_add_style(page2_obj, &style, 0);
    lv_obj_set_pos(page2_obj, 0, 0);
    lv_obj_set_size(page2_obj, 200, 200);
    lv_obj_add_event_cb(page2_obj, event_handler, LV_EVENT_CLICKED, NULL);
}

2、解决办法

每次在页面切换时,使用接口将style其释放
解决代码:
取消注释event_handler中的lv_style_reset(&style);
现象:
no_leak.gif

3、总结

  • 造成内存泄露的核心原因是用户创建的style没有释放,因此在页面切换时不要忘记释放用户定义的style
  • 如果这个style修饰了多个对象,切换界面时释放style仍然会造成内存泄漏,因此在动态界面切换过程中,一个style最好只用来修饰一个界面,除非你知道你正在做什么
  • lvgl的style注释中也详细交代了Do not call lv_style_init on styles that already have some properties because this function won't free the used memory, just sets a default state for the style. In other words be sure to initialize styles only once!
  • 在开发阶段可以尽量多的达赖lvgl的调试宏开关

    • ·#define LV_USE_LOG 1 开启log开关
    • ·#define LV_USE_ASSERT_MALLOC 1 如果内存耗尽,会进入断言
    • ·define LV_USE_ASSERT_STYLE 1 如果出现了style重新初始化,会打印log提醒(本文核心)
文章目录