事故起因

代码运行后没几分钟系统就崩溃重启,看了一下是内存分配不出来了,通过不断注释代码排除后锁定到cJSON库上。

问题调查1

看了一下cJSON库中的注释,发现这么一句话:
Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer.
Supply a block of JSON, and this returns a cJSON object you can interrogate.
大概意思是调用cJSON_ParsecJSON_Print与这两个函数的任何变体时,调用者需自行释放内存
于是写了这么两个函数

void *kvstore_print(KeyValueStore *kvstore, char *json_str)
{
    char *str = cJSON_Print(kvstore->root);
    strcpy(json_str, str);
    cJSON_free(str);
}
int linkkit_get_value_int(const char *json_str, char *key, int *value)
{
    cJSON *root = NULL;
    cJSON *item = NULL;

    root = cJSON_Parse(json_str);
    if (root == NULL)
    {
        printf("parse json failed\n");
        return -1;
    }
    item = cJSON_GetObjectItem(root, key);
    if (item == NULL)
    {
        printf("No key\n");
        return -2;
    }
    *(int *)value = item->valueint;
    cJSON_Delete(root);
    return 0;
}

问题调查2

经过在上面这两个函数中添加释放内存的操作后,系统没有那么快崩溃了,但是我不停查看剩余内存后发现内存依然在泄露,于是上网查找其他大佬的心得,看到了这篇文章

原来在使用cJSON_Create及其变体时也需要手动释放内存,于是修改函数如下:

void kvstore_add_str(KeyValueStore *kvstore, const char *key, const char *value)
{
    if (kvstore && key)
    {
        cJSON *val = cJSON_CreateString(value);
        kvstore_add(kvstore, key, val);
        cJSON_Delete(val);
    }
}

问题调查3

经过前面两步,我观察到每走一次业务流程的内存泄露从5.5KBytes变成了64Bytes,这么稳定且小的内存泄漏很容易发现原因:初始kvstore时手动分配了一个KeyValueStore的结构体,在反初始化代码中添加一句free即可解决

void kvstore_free(KeyValueStore *kvstore)
{
    if (kvstore)
    {
        cJSON_Delete(kvstore->root);
        cJSON_free(kvstore);
    }
}

总结

  1. 在调用cJSON_ParsecJSON_PrintcJSON_Create这三个函数及其变体时,使用者需要手动释放其返回值的内存
  2. 在KeyValueStore结构体使用完毕后,需要手动释放其自身