项目场景:
之前为了方便 SDDC 协议使用,我自己写了一个 SDDC 的 SDK, 具体详见:同人逼死官方系列!基于sddc 协议的SDK框架 sddc_sdk_lib 解析 和 同人逼死官方系列 从 DDC 嗅探器到 sddc_sdk_lib 的数据解析 ,但是当时对 cjson 的使用还不是很熟悉,导致出现了一个内存泄露的问题,导致了ESP32运行一段时间后无法收到报文。
问题描述
在频繁收到消息命令,调用 sddc_on_message_lib 函数后,大概半小时左右 ESP32 将会收不到任何报文,通过打印发现 select 一直返回 0 ,这个问题一度让我非常费解。
网上最多的回答是 FD_SET 没有重置导致的,可是我检查了 sddc 代码,官方确实做了循环调用 FD_SET ,没有问题,不是这个原因。
射频信号确实发出来了。
ESP32底层也不应该有问题。
当时出现问题的是一个 I2C设备。导致我一直在怀疑是 I2C 导致的问题,别说还真有可能,ESP32 的 I2C 有可能会受到干扰 I2C 《 调试经验分享》然后我苦兮兮的去检测 I2C ,发现也是正常的。这就很令人费解了啊。
最后我无意中发现另外一个 通过 GPIO 读取数据的设备在长时间运行后也出现这样的问题。我对比了这两个设备和其他没问题的设备,最后发现差异在触发 "get" 命令的频率上,而 "get" 是一个命令,会触发消息处理回调函数,这样我才把注意力调到了回调处理流程上,然后发现了一处内存泄漏  ̄□ ̄||。
原因分析:
内存泄露位于 SDDC_SDK_lib.c 中的 sddc_on_message_lib ,里面调用的 cJSON_Parse 函数会在次级函数中申请内存给root,需要手动释放一下。
(1)使用root = cJSON_Parse(text); //将文本转成json格式,次函数里面申请了一块内存给root
所以在最后要释放rootcJSON_Delete(root ); //释放cJSON_Parse()分配出来的内存空间
(2)使用str =
cJSON_Print(root);//次函数将json数据转成字符串,这个函数内申请了一段内存给out,所以使用完out后也要释放(3)使用cJSON *new_json_str =
cJSON_CreateString(str);//将一个字符串转成一个json对象,函数里面也涉及了内存分配,座椅用完以后也要释放cJSON_Delete(new_json_str
需要注意:cJSON_CreateObject创建的指针,需要使用cJSON_Delete删除,cJSON_Print赋值的指针需要free释放。对应不上是没办法真正释放的。
解决方案:
修改后的 sddc_on_message_lib 代码如下:其实就是在每一个出口处加了一个 cJSON_Delete(root); 函数释放内存。
static sddc_bool_t sddc_on_message_lib(sddc_t *sddc, const uint8_t *uid, const char *message, size_t len)
{
cJSON *root = cJSON_Parse(message);
cJSON *Json_method;
uint8_t uimethod =DDC_METHOD_VALIDE;
Json_method = cJSON_GetObjectItem(root, "method");
if (NULL == Json_method) {
cJSON_Delete(root);
return SDDC_FALSE;
}
if (cJSON_IsString(Json_method)) {
if (strcmp(Json_method->valuestring,"set") == 0) {
uimethod = DDC_METHOD_SET;
} else if (strcmp(Json_method->valuestring,"get") == 0) {
uimethod = DDC_METHOD_GET;
} else {
cJSON_Delete(root);
return SDDC_FALSE;
}
} else {
cJSON_Delete(root);
return SDDC_FALSE;
}
if (uimethod == DDC_METHOD_VALIDE) {
cJSON_Delete(root);
return SDDC_FALSE;
}
if (uimethod == DDC_METHOD_SET) {
int i;
/*
* 数字量、显示量先查询设置,防止开关量是设备的使能
*/
for (i=0; i<G_config->num_dev_reg_num; i++) {
object_Number_Set(root, G_config->num_dev_reg[i].objname, G_config->num_dev_reg[i].Num_Fun);
}
for (i=0; i<G_config->dis_dev_num; i++) {
object_Display_Set(root, G_config->dis_dev_reg[i].objname, G_config->dis_dev_reg[i].Dis_Fun);
}
for (i=0; i<G_config->io_dev_reg_num; i++) {
object_IO_Set(root, G_config->io_dev_reg[i].objname, G_config->io_dev_reg[i].IO_Fun);
}
} else if (uimethod == DDC_METHOD_GET) {
object_get(sddc, uid, root, G_config->state_get_reg, G_config->state_get_reg_num, 0);
} else {
cJSON_Delete(root);
return SDDC_FALSE;
}
cJSON_Delete(root);
return SDDC_TRUE;
}