what is the proper way of free memory in creating json request from jansson libary?

3.4k Views Asked by At

i am using janson library to send json body as rest request's , i have notice that i am doing it in this way :

json_t *json_body = json_object();
char sentString[100];
char sentStringSecond[100];
..
json_object_set_new(json_body, "sentString", json_string(sentString));
json_object_set_new(json_body, "sentStringSecond", json_string(sentStringSecond);
..
json_decref(json_body);     

is this one call to json_decref is enough to free all of the memory ? my concern are mainly after reading this post json_decref not freeing memory?

2

There are 2 best solutions below

4
On BEST ANSWER

You are using a different function to set the JSON strings: json_object_set_new instead of json_object_set from the linked question.

Your version adds the element to the json-object and "reuses" the reference. It does not increment the counter. Therefore you don't need to decrement it manually. The comment below the answer of that question also mentions json_object_set_new.

The added element will be free'd together with the main JSON object json_body. This meand the reference counters of all sub-objects are decremented automatically. If a counter drops to 0, the object is free'd.

If you hold a reference to a sub-object on your own, the object will not be free'd.

Just an example:

You create an object (call json_string()) and it will get reference count==1.

Variant a) (from the linked question)

You add this object to another object with a new reference. This is done using json_object_set. Then the counter is incremented to 2.

If you delete the main object (json_decref(json_body)) The counter goes down to 1 again but the string is still not released. You need to use json_decref(srting) to free the memory.

Variant b) (your code)

You add this object to another object with handing over your existing reference. This is done using json_object_set_new. Then the counter is preserved at value 1.

If you delete the main object (json_decref(json_body)) The counter goes down to 0 and now the string is released together with the main object. You do not need to use json_decref(srting) to free the memory.

0
On

When we create a json object in jansson library, it is of type 'json_t'. This lib uses reference counting to manage the cleanup of the object. It internally calls malloc and free for memory management; whenever we create an object, json_t * obj the memory is allocated and it will not free the object till the reference count is not null.

The term stealing reference which is mentioned in the documentation quite often means that the object reference count is borrowed and it will not increase the count once the object is consumed. So, all the ".._new" api in the lib lets you write the in a cleaner way without the fuss of decref -ing the individual elements in the json object.

I myself have to read to docs and examples to create this consolidated answer for this json_decref. I have tried to keep it simple and have used valgrind to get the better understanding. Below are the examples:

  1. when a child object is created as node in json object (parent) -> cleaning parent object cleans child as well
  2. when multiple json objects are added to a json array object -> cleaning json array object cleans the subordinate nodes
  3. when '_new' api is not used and object is created with e.g 'json_object_set(..)' -> explicitly clean the individual element

Example 1:

code

#include <stdio.h>
#include <stdlib.h>
#include <jansson.h>
    
int main(){
    
    int a =5;
    //char * str = malloc(sizeof(a)*4);
    printf("str allocated\n");
    json_t * obj = json_object();
    json_t * obj2 = json_object();
    json_object_set_new(obj, "node", obj2);
    json_object_set_new(obj, "num", json_integer(a));
    json_object_set_new(obj, "num", json_integer(a));
    json_object_set_new(obj2, "num3", json_integer(2));
    json_object_set_new(obj2, "num4", json_integer(28));
    json_object_set_new(obj, "str", json_string("name"));
    
     char * s = json_dumps(obj, JSON_COMPACT | JSON_INDENT(2));
            printf("json is [%s]", s);
            free(s);
    //json_decref(obj);
    json_decref(obj2);
    
    return 0;
}

cli output

charulata@charulata-virtual-machine:~$ vi test_val_json.c 
charulata@charulata-virtual-machine:~$ gcc -o json_v test_val_json.c -ljansson 
charulata@charulata-virtual-machine:~$ valgrind ./json_v
==5676== Memcheck, a memory error detector
==5676== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5676== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==5676== Command: ./json_v
==5676== 
str allocated
json is [{
  "node":{
    "num3":2,
    "num4":28
  },
  "num":5,
  "str":"name"
}]==5676== 
==5676== HEAP SUMMARY:
==5676==     in use at exit: 418 bytes in 8 blocks
==5676==   total heap usage: 24 allocs, 16 frees, 2,378 bytes allocated
==5676== 
==5676== LEAK SUMMARY:
==5676==    definitely lost: 72 bytes in 1 blocks
==5676==    indirectly lost: 346 bytes in 7 blocks
==5676==      possibly lost: 0 bytes in 0 blocks
==5676==    still reachable: 0 bytes in 0 blocks
==5676==         suppressed: 0 bytes in 0 blocks
==5676== Rerun with --leak-check=full to see details of leaked memory
==5676== 
==5676== For lists of detected and suppressed errors, rerun with: -s
==5676== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Example 2:

code

#include <stdio.h>
#include <stdlib.h>
#include <jansson.h>
    
int main(){
    
    json_t * res = json_object();
    json_object_set_new(res, "state", json_string("up"));
    json_t * jcards = json_array();
    json_object_set_new(res, "cards", jcards);
    
    for ( int i = 0; i <2; i++){
    
    int a =5;
    //char * str = malloc(sizeof(a)*4);
    printf("str allocated\n");
    json_t * obj = json_object();
    json_array_append_new(jcards, obj);
    json_object_set_new(obj, "num", json_integer(a));
    json_object_set_new(obj, "num", json_integer(a));
    json_object_set_new(obj, "num3", json_integer(2));
    json_object_set_new(obj, "num4", json_integer(2));
    json_object_set_new(obj, "str", json_string("name"));
    
    
    json_t * new = json_object_get(res, "cards");
     char * k = json_dumps(new, JSON_COMPACT | JSON_INDENT(2));
            printf("json is [%s]", k);
            free(k);
    
     char * s = json_dumps(obj, JSON_COMPACT | JSON_INDENT(2));
            printf("json is [%s]", s);
            free(s);
    //json_decref(obj); /*not required as parent obj is being cleaned*/
    }
    //json_decref(jcards); /*not required as parent obj is being cleaned, arr is part of json obj here 'res'*/ 
    json_decref(res);
    
    return 0;
}

cli output

charulata@charulata-virtual-machine:~$ vi test_json_arr_obj.c 
charulata@charulata-virtual-machine:~$ gcc -o json_arr test_json_arr_obj.c -ljansson 
charulata@charulata-virtual-machine:~$ valgrind ./json_arr 
==5798== Memcheck, a memory error detector
==5798== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5798== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==5798== Command: ./json_arr
==5798== 
str allocated
json is [[
  {
    "num":5,
    "num3":2,
    "num4":2,
    "str":"name"
  }
]]json is [{
  "num":5,
  "num3":2,
  "num4":2,
  "str":"name"
}]str allocated
json is [[
  {
    "num":5,
    "num3":2,
    "num4":2,
    "str":"name"
  },
  {
    "num":5,
    "num3":2,
    "num4":2,
    "str":"name"
  }
]]json is [{
  "num":5,
  "num3":2,
  "num4":2,
  "str":"name"
}]==5798== 
==5798== HEAP SUMMARY:
==5798==     in use at exit: 0 bytes in 0 blocks
==5798==   total heap usage: 63 allocs, 63 frees, 4,750 bytes allocated
==5798== 
==5798== All heap blocks were freed -- no leaks are possible
==5798== 
==5798== For lists of detected and suppressed errors, rerun with: -s
==5798== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Example 3:

case 1 : mem leak

#include <stdio.h>
#include <stdlib.h>
#include <jansson.h>
    
int main(){ 

    int a =5;
    json_t * obj = json_object();
    //json_object_set_new(obj, "num3", json_integer(2));
    json_object_set(obj, "num3", json_integer(2));  /*this json_integer object is not freed*/
    json_object_set_new(obj, "num4", json_integer(2));
    json_object_set_new(obj, "str", json_string("name"));
    
    
    char * s = json_dumps(obj, JSON_COMPACT | JSON_INDENT(2));
    printf("json is [%s]", s);
    free(s);
    
    json_decref(obj);
    
    return 0;
}

cli output

charulata@charulata-virtual-machine:~$ vi test_json_set_new.c 
charulata@charulata-virtual-machine:~$ gcc -o json_s_new test_json_set_new.c -ljansson 
charulata@charulata-virtual-machine:~$ valgrind ./json_s_new 
==6274== Memcheck, a memory error detector
==6274== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6274== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==6274== Command: ./json_s_new
==6274== 
json is [{
  "num3":2,
  "num4":2,
  "str":"name"
}]==6274== 
==6274== HEAP SUMMARY:
==6274==     in use at exit: 24 bytes in 1 blocks
==6274==   total heap usage: 16 allocs, 15 frees, 1,808 bytes allocated
==6274== 
==6274== LEAK SUMMARY:
==6274==    definitely lost: 24 bytes in 1 blocks
==6274==    indirectly lost: 0 bytes in 0 blocks
==6274==      possibly lost: 0 bytes in 0 blocks
==6274==    still reachable: 0 bytes in 0 blocks
==6274==         suppressed: 0 bytes in 0 blocks
==6274== Rerun with --leak-check=full to see details of leaked memory
==6274== 
==6274== For lists of detected and suppressed errors, rerun with: -s
==6274== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

case 2: decref-ing individual element

#include <stdio.h>
#include <stdlib.h>
#include <jansson.h>
    
int main(){
    
    int a =5;
    json_t * obj = json_object();
    json_t *val = json_integer(2);
    json_object_set(obj, "num3", val);  /*this json_integer object is not freed by itself, exclusively decref is called for this element*/
    json_object_set_new(obj, "num4", json_integer(3));
    json_object_set_new(obj, "str", json_string("name"));
    
    
    char * s = json_dumps(obj, JSON_COMPACT | JSON_INDENT(2));
    printf("json is [%s]", s);
    free(s);
    
    json_decref(obj);
    json_decref(val);
    
    return 0;
}

cli output

charulata@charulata-virtual-machine:~$ gcc -o json_s_new test_json_set_new.c -ljansson 
charulata@charulata-virtual-machine:~$ valgrind ./json_s_new 
==6515== Memcheck, a memory error detector
==6515== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6515== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==6515== Command: ./json_s_new
==6515== 
json is [{
  "num3":2,
  "num4":3,
  "str":"name"
}]==6515== 
==6515== HEAP SUMMARY:
==6515==     in use at exit: 0 bytes in 0 blocks
==6515==   total heap usage: 16 allocs, 16 frees, 1,808 bytes allocated
==6515== 
==6515== All heap blocks were freed -- no leaks are possible
==6515== 
==6515== For lists of detected and suppressed errors, rerun with: -s
==6515== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

case 3 : use '_new' api for stealing reference

#include <stdio.h>
#include <stdlib.h>
#include <jansson.h>
    
int main(){
    
    int a =5;
    json_t * obj = json_object();
    json_t *val = json_integer(2);
    json_object_set(obj, "num3", val);  /*this json_integer object is not freed by itself, exclusively decref is called for this element*/
    json_object_set_new(obj, "num3", val);  /*this json_integer object is freed by itself*/
    json_object_set_new(obj, "num4", json_integer(3));
    json_object_set_new(obj, "str", json_string("name"));
    
    char * s = json_dumps(obj, JSON_COMPACT | JSON_INDENT(2));
    printf("json is [%s]", s);
    free(s);
    
    json_decref(obj);
    //json_decref(val);
    
    return 0;
}

cli output

charulata@charulata-virtual-machine:~$ gcc -o json_s_new test_json_set_new.c -ljansson 
charulata@charulata-virtual-machine:~$ valgrind ./json_s_new 
==6533== Memcheck, a memory error detector
==6533== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6533== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==6533== Command: ./json_s_new
==6533== 
json is [{
  "num3":2,
  "num4":3,
  "str":"name"
}]==6533== 
==6533== HEAP SUMMARY:
==6533==     in use at exit: 0 bytes in 0 blocks
==6533==   total heap usage: 16 allocs, 16 frees, 1,808 bytes allocated
==6533== 
==6533== All heap blocks were freed -- no leaks are possible
==6533== 
==6533== For lists of detected and suppressed errors, rerun with: -s
==6533== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)