LVGL版本:V8.0.2
平台:ESP32S3

在调试过程中,发现有两个界面,在重复退出再进入时内存会不断增加的吃内存现象,然后做了分析和研究。

1. 样式style吃内存

在主页面,进入simple页面,再退出到主页面,再次进入simple,重复几次,内存就肉眼可见的增加。从开机时主页面5.3KB,增加到了6.6KB。

luajit 内存 lvgl内存咋使用那么多_LVGL


luajit 内存 lvgl内存咋使用那么多_LVGL_02


luajit 内存 lvgl内存咋使用那么多_gui_03


看下style部分的函数注释就明白了:

/**
 * Initialize a style
 * @param style pointer to a style to initialize
 * @note Do not call `lv_style_init` on styles that are already have some properties
 *       because this function won't free the used memory just set a default state for the style.
 *       In other words be sure to initialize styles only once!
 */
void lv_style_init(lv_style_t * style);

/**
 * Clear all properties from a style and free all allocated memories.
 * @param style pointer to a style
 */
void lv_style_reset(lv_style_t * style);

在创建控件的时候用lv_style_init创建了很多样式,部分源码移驾这里,但是析构页面的时候样式部分的内存还在,下一次进入又重复创建样式,导致内存泄露。解决办法,析构页面的时候要释放lv_style_init申请的内存。

解决方案:
  1. lv_style_reset,释放style部分内存
  2. delete页面的父对象,即页面容器对象。此操作会一并delete该父对象的所有子对象
  3. free该页面的数据结构
/*
 * brief:   界面销毁
 * param:   父对象
 * retval:  NULL
 */
void winSimpleDestroy(lv_obj_t * pObjParent){
    LV_UNUSED(pObjParent);
    if(NULL != pWinObj){
        lv_style_reset(&pWinObj->label_styles);
        lv_style_reset(&pWinObj->style_arc1_bg);
        lv_style_reset(&pWinObj->style_arc1_indic);
        lv_style_reset(&pWinObj->style_arc1_knob);
        lv_style_reset(&pWinObj->style_arc2_bg);
        lv_style_reset(&pWinObj->style_arc2_indic);
        lv_style_reset(&pWinObj->style_arc2_knob);
        lv_style_reset(&pWinObj->style_bar_label1);
        lv_style_reset(&pWinObj->style_bar1_bg);
        lv_style_reset(&pWinObj->style_bar1_indic);
        lv_style_reset(&pWinObj->style_checkbox1);
        lv_style_reset(&pWinObj->style_spinner1_bg);
        lv_style_reset(&pWinObj->style_spinner1_indic);
        lv_style_reset(&pWinObj->style_spinner2_bg);
        lv_style_reset(&pWinObj->style_spinner2_indic);
        lv_style_reset(&pWinObj->style_line1);
        lv_style_reset(&pWinObj->style_led1);

        lv_obj_del(pWinObj->pCurrWinObj);
        lv_mem_free(pWinObj);
        pWinObj = NULL;
    }
    MEM_PRINT;
}

2. table控件吃内存

在主页面,进入WiFi页面,再退出到主页面,再次进入WiFi页面,重复多次,内存增加,虽然没有simple页面那么快,但是还是会一点点的耗内存。

luajit 内存 lvgl内存咋使用那么多_控件_04


luajit 内存 lvgl内存咋使用那么多_luajit 内存_05


luajit 内存 lvgl内存咋使用那么多_gui_06


该WiFi页面也没有用lv_style_init呀,怎么还会造成内存泄漏呢?

只能一个个控件排查了,最先怀疑的就是table控件咯,因为其他控件在其他页面也使用过,没有内存泄漏,果然,把table屏蔽之后内存不会泄漏了。只能从源码入手分析了。

分析table控件的构造和析构函数,果然发现端倪了

  • 构造函数中申请的内存,在析构函数中没有释放完全
static void lv_table_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
    LV_UNUSED(class_p);
    LV_TRACE_OBJ_CREATE("begin");

    lv_table_t * table = (lv_table_t *)obj;

    table->col_cnt = 1;
    table->row_cnt = 1;
    table->col_w = lv_mem_alloc(table->col_cnt * sizeof(table->col_w[0]));
    table->row_h = lv_mem_alloc(table->row_cnt * sizeof(table->row_h[0]));
    table->col_w[0] = LV_DPI_DEF;
    table->row_h[0] = LV_DPI_DEF;
    table->cell_data = lv_mem_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(char *));
    table->cell_data[0] = NULL;

    LV_TRACE_OBJ_CREATE("finished");
}

static void lv_table_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
    LV_UNUSED(class_p);
    lv_table_t * table = (lv_table_t *)obj;
    /*Free the cell texts*/
    uint16_t i;
    for(i = 0; i < table->col_cnt * table->row_cnt; i++) {
        if(table->cell_data[i]) {
            lv_mem_free(table->cell_data[i]);
            table->cell_data[i] = NULL;
        }
    }

    if(table->cell_data) lv_mem_free(table->cell_data);
    if(table->row_h) lv_mem_free(table->row_h);
    // if(table->col_w) lv_mem_free(table->col_w); // fix | slim20221007 > 会导致内存泄露
}
解决方案:

加入如下patch就好了,重复进出WiFi页面,测试多次,内存稳定了

if(table->col_w) lv_mem_free(table->col_w); // fix | slim20221007 > 会导致内存泄露