cJSON 释放 cJSON 对象时内存泄漏

问题描述

我在使用 cJSON 库时遇到问题。我假设存在内存泄漏,在一段时间(40 分钟到 1 小时)后破坏了代码

我在下面复制了我的代码

void my_work_handler_5(struct k_work *work)
{

     char *ptr1[6];
     int y=0;
     static int counterdo = 0;
     char *desc6 = "RSRP";
     char *id6 = "dBm";
     char *type6 = "RSRP";
     char rsrp_str[100];
     snprintf(rsrp_str,sizeof(rsrp_str),"%d",rsrp_current);

     sensor5 = cJSON_CreateObject();

    cJSON_AddItemToObject(sensor5,"description",cJSON_CreateString(desc6));
    cJSON_AddItemToObject(sensor5,"Time",cJSON_CreateString(time_string));
    cJSON_AddItemToObject(sensor5,"value",cJSON_CreateNumber(rsrp_current));
    cJSON_AddItemToObject(sensor5,"unit",cJSON_CreateString(id6));
    cJSON_AddItemToObject(sensor5,"type",cJSON_CreateString(type6));

       /* print everything */
        ptr1[counterdo] = cJSON_Print(sensor5);

        printk("Counterdo value is : %d\n",counterdo);
         
        cJSON_Delete(sensor5);
        
         counterdo = counterdo + 1;
        
        if (counterdo==6){
        for(y=0;y<=counterdo;y++){
         free(ptr1[y]);
         }
         counterdo = 0;
         }

        return;
}

我阅读了一些关于释放内存的其他线程,并尝试做同样的事情。谁能告诉我这是否是释放分配给 cJSON 对象的空间的正确方法

问候, 阿迪尔。

解决方法

由于 cJSON 是一个没有依赖项的可移植库,因此最好在 PC 上查找代码中的潜在问题:它们是此环境中可用于促进调查的专用工具。我在这里假设你有一个 Linux 系统,一个安装了 WSL 或 WSL2 的 Windows 系统,或者一个可用的 Linux 虚拟机,并且安装了 gcc、valgrind。

您的代码的最小、自包含、可移植版本可能是:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <cJSON.h>

static int rsrp_current = 1;
static char *time_string = NULL;

void
my_work_handler_5 ()
{
  char *ptr1[6];
  int y = 0;
  static int counterdo = 0;
  char *desc6 = "RSRP";
  char *id6 = "dBm";
  char *type6 = "RSRP";
  char rsrp_str[100];
  snprintf (rsrp_str,sizeof (rsrp_str),"%d",rsrp_current);

  cJSON *sensor5 = cJSON_CreateObject ();

  cJSON_AddItemToObject (sensor5,"description",cJSON_CreateString (desc6));
  cJSON_AddItemToObject (sensor5,"Time",cJSON_CreateString (time_string));
  cJSON_AddItemToObject (sensor5,"value",cJSON_CreateNumber (rsrp_current));
  cJSON_AddItemToObject (sensor5,"unit",cJSON_CreateString (id6));
  cJSON_AddItemToObject (sensor5,"type",cJSON_CreateString (type6));

  /* print everything */
  ptr1[counterdo] = cJSON_Print (sensor5);

  printf ("Counterdo value is : %d\n",counterdo);

  cJSON_Delete (sensor5);

  counterdo = counterdo + 1;

  if (counterdo == 6)
    {
      for (y = 0; y <= counterdo; y++)
    {
      free (ptr1[y]);
    }
      counterdo = 0;
    }

  return;
}

int
main (int argc,char **argv)
{
  time_t curtime;

  time (&curtime);

  for (int n = 0; n < 3 * 6; n++)
    {
      my_work_handler_5 ();
    }
}

构建过程:

wget https://github.com/DaveGamble/cJSON/archive/v1.7.14.tar.gz
tar zxf v1.7.14.tar.gz
gcc -g -O0 -IcJSON-1.7.14 -o cjson cjson.c cJSON-1.7.14/cJSON.c

在程序上运行 valgrind:

valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose   ./cjson

.. 表示正在释放一些先前未分配的内存:Invalid free() / delete / delete[] / realloc():

==6747== 
==6747== HEAP SUMMARY:
==6747==     in use at exit: 0 bytes in 0 blocks
==6747==   total heap usage: 271 allocs,274 frees,14,614 bytes allocated
==6747== 
==6747== All heap blocks were freed -- no leaks are possible
==6747== 
==6747== ERROR SUMMARY: 21 errors from 2 contexts (suppressed: 0 from 0)
==6747== 
==6747== 3 errors in context 1 of 2:
==6747== Invalid free() / delete / delete[] / realloc()
==6747==    at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==6747==    by 0x1094DA: my_work_handler_5 (cjson.c:42)
==6747==    by 0x10955A: main (cjson.c:59)
==6747==  Address 0x31 is not stack'd,malloc'd or (recently) free'd
==6747== 
==6747== 
==6747== 18 errors in context 2 of 2:
==6747== Conditional jump or move depends on uninitialised value(s)
==6747==    at 0x483C9F5: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==6747==    by 0x1094DA: my_work_handler_5 (cjson.c:42)
==6747==    by 0x10955A: main (cjson.c:59)
==6747==  Uninitialised value was created by a stack allocation
==6747==    at 0x109312: my_work_handler_5 (cjson.c:11)
==6747== 
==6747== ERROR SUMMARY: 21 errors from 2 contexts (suppressed: 0 from 0)

替换:

  for (y = 0; y <= counterdo; y++)
{
  free (ptr1[y]);
}

作者:

  for (y = 0; y < counterdo; y++)
{
  free (ptr1[y]);
}

并再次执行 valgrind:

==6834== 
==6834== HEAP SUMMARY:
==6834==     in use at exit: 1,095 bytes in 15 blocks
==6834==   total heap usage: 271 allocs,256 frees,614 bytes allocated
==6834== 
==6834== Searching for pointers to 15 not-freed blocks
==6834== Checked 75,000 bytes
==6834== 
==6834== 1,095 bytes in 15 blocks are definitely lost in loss record 1 of 1
==6834==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==6834==    by 0x10B161: print (cJSON.c:1209)
==6834==    by 0x10B25F: cJSON_Print (cJSON.c:1248)
==6834==    by 0x1094AB: my_work_handler_5 (cjson.c:30)
==6834==    by 0x10959C: main (cjson.c:59)
==6834== 
==6834== LEAK SUMMARY:
==6834==    definitely lost: 1,095 bytes in 15 blocks
==6834==    indirectly lost: 0 bytes in 0 blocks
==6834==      possibly lost: 0 bytes in 0 blocks
==6834==    still reachable: 0 bytes in 0 blocks
==6834==         suppressed: 0 bytes in 0 blocks
==6834== 
==6834== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

某些内存确实被泄露了。

原因是 char *ptr1[6] 不是静态的,因此每次调用 my_work_handler_5() 时都会在堆栈上创建。 cJSON_Print() 返回的指针因此在两次调用之间丢失,并且 free() 在任意指针值上被调用,因为 ptr1[] 没有像它可能的那样初始化:

char *ptr1[6] = { NULL,NULL,NULL };

由于您每 6 次调用就释放内存,这会导致您怀疑的内存泄漏。

替换:

char *ptr1[6];

作者:

static char *ptr1[6];

编译,再次运行valgrind:

==6927== 
==6927== HEAP SUMMARY:
==6927==     in use at exit: 0 bytes in 0 blocks
==6927==   total heap usage: 271 allocs,271 frees,614 bytes allocated
==6927== 
==6927== All heap blocks were freed -- no leaks are possible
==6927== 
==6927== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

该程序的修改版本现在应该可以在您的裸机系统上运行了。