(json-c學習7) linux c語言釋放json對象,防止內存泄漏

實際項目中發現Json-C用法不當導致的內存泄露、踩內存問題,大都是因爲不清楚下面幾個接口的用法。
以下分析基於https://github.com/json-c/json-c( 0.12.1 release)。

1. json_object_new_object生成的對象要不要釋放
int main(int argc, char **argv)
{
    struct json_object* obj;
    mtrace();
    obj = json_object_new_object();
    //json_object_put(obj);
    return 0;
}

上面的代碼執行後,你會發現泄漏下面這些內存:

Memory not freed:
-----------------
           Address     Size     Caller
0x0000000000b6a460     0x48  at json-c-json-c-0.12.1-20160607/json_object.c:185
0x0000000000b6a4b0     0x58  at json-c-json-c-0.12.1-20160607/linkhash.c:435
0x0000000000b6a510    0x200  at json-c-json-c-0.12.1-20160607/linkhash.c:440

所以,json_object_new_object生成的對象必須調用json_object_put釋放。

2. json_tokener_parse生成的對象要不要釋放
int main(int argc, char **argv)
{
    mtrace();
    const char *str = "{\"a\":1}";
    struct json_object* obj = json_tokener_parse(str);
    //json_object_put(obj);
    return 0;
}

上面這些代碼執行後,你會發現下面這些 內存泄漏:

Memory not freed:
-----------------
           Address     Size     Caller
0x00000000022e7930     0x48  at json-c-json-c-0.12.1-20160607/json_object.c:185
0x00000000022e7980     0x58  at json-c-json-c-0.12.1-20160607/linkhash.c:435
0x00000000022e79e0    0x200  at json-c-json-c-0.12.1-20160607/linkhash.c:440
0x00000000022e7c10     0x48  at json-c-json-c-0.12.1-20160607/json_object.c:185

所以,json_tokener_parse生成的對象,必須使用json_object_put釋放.

3. json_object_object_get出來的對象要不要釋放
int main(int argc, char **argv)
{
    struct json_object* obj;
    struct json_object *child;

    obj = json_object_new_object();

    json_object_object_add(obj, "a", json_object_new_int(1));
    json_object_object_add(obj, "b", json_object_new_int(2));

    child = json_object_object_get(obj,"a");
    json_object_put(child);     //Oh, No!!!
    json_object_put(obj);
    return 0;
}

藉助內存越界檢測工具efence和gdb,運行代碼發現段錯誤,其中test.c:22指向json_object_put(obj)這一行.
這是因爲child節點被釋放過了,現在又去釋放, 使用了野指針(不借助工具,程序會正常結束,這也是這種錯誤的可怕之處)。
這種不會立即終止程序的錯誤太可怕 ,讓你都不知道怎麼死的。

Program received signal SIGSEGV, Segmentation fault.
json_object_put (jso=0x7ffff7ee2fb8) at json_object.c:154
154                     jso->_ref_count--;
(gdb) bt
#0  json_object_put (jso=0x7ffff7ee2fb8) at json_object.c:154
#1  0x0000000000403346 in lh_table_free (t=0x7ffff7edefa8) at linkhash.c:485
#2  0x000000000040190d in json_object_object_delete (jso=0x7ffff7edcfb8) at json_object.c:354
#3  0x0000000000401edd in json_object_put (jso=0x7ffff7edcfb8) at json_object.c:159
#4  json_object_put (jso=0x7ffff7edcfb8) at json_object.c:150
#5  0x0000000000401515 in main (argc=1, argv=0x7fffffffdfd8) at test.c:22

所以,通過json_object_object_get獲取的對象不能單獨釋放,因爲它仍然歸父節點所有。

4. 通過json_object_object_add添加到其他節點的,能不能釋放
int main(int argc, char **argv)
{
    struct json_object* obj;
    struct json_object *child;

    child = json_object_new_object();

    obj = json_object_new_object();
    json_object_object_add(obj, "a", json_object_new_int(1));
    json_object_object_add(obj, "child", child);

    json_object_put(child);     //Oh, No!!!
    json_object_put(obj);
    return 0;
}

這個運行後,產生的錯誤和3中類似,也是因爲重複釋放。
所以,通過json_object_object_add添加到其他節點的不能再單獨釋放,因爲他已經成爲別人的子節點,他的生命週期由父節點維護了。

5. json_object_to_json_string獲取到的字串要不要釋放
int main(int argc, char **argv)
{
    struct json_object* obj;
    char *str;
    obj = json_object_new_object();
    json_object_object_add(obj, "a", json_object_new_int(1));
    json_object_object_add(obj, "b", json_object_new_int(2));
    str =  json_object_to_json_string(obj);

    free(str);     //Oh, No!!!
    json_object_put(obj);
    return 0;
}


這個free也是非法的,因爲json_object_to_json_string只是把json對象內部的指針暴露給你了,借你用下而已,千萬別釋放。

6. Other
上面這幾點疑惑,通過API接口描述文檔都可以消除掉,再不濟看看作者的Demo、源碼也可以消除掉。
所以,大家使用開源軟件時,一定要搞明白再用,否則會帶來很多問題。
————————————————
版權聲明:本文爲CSDN博主「huojianying123456」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/huojianying123456/article/details/66472132

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章