Jansson库json_decref异常行为及引用管理问题咨询
关于Jansson库
json_decref使用的疑问解答 先直接理清Jansson引用计数的核心规则,这是解决你问题的关键:
- 拥有所有权的引用:通过
json_load_file、json_object_new、json_integer等创建对象的函数返回的引用,你需要负责调用json_decref来释放。 - 借用引用:通过
json_object_get、json_array_get等获取对象中元素的函数返回的引用,你不需要调用json_decref,因为所有权仍然属于原对象(比如你的parObj)。
问题1:为什么cleanJson()后getAccountId仍能返回旧值?
当你调用cleanJson()执行json_decref(parObj)时,只是把parObj的引用计数减1。如果这是最后一个引用,Jansson会释放parObj占用的内存,但被释放的内存并不会立即被操作系统清空或覆盖——你的程序此时访问这块内存,可能还能读到之前存储的值,这属于未定义行为。
这种情况完全是巧合,下次运行程序可能就会返回随机值、崩溃,或者返回null,不能依赖这个结果。你预期的“返回错误或null”是合理的,但未定义行为的表现本身就是不确定的。
问题2:给json_acc和json_param加json_decref导致提前崩溃
json_acc是json_object_get(parObj, "accounts")返回的借用引用,json_param同理——它们的所有权都属于parObj。如果你对它们调用json_decref,会错误地减少它们的引用计数,可能导致这些子对象被提前释放。
当后续代码(甚至是json_object_get内部)访问已经被释放的内存时,就会触发崩溃。崩溃点不一定在你预期的a_test调用环节,因为内存损坏的影响是连锁且不确定的,可能在第一次访问被破坏的对象时就崩溃了。
正确的代码示例
你的核心代码只需要在parObj上调用一次json_decref即可,不需要处理json_acc和json_param:
API侧修正后代码
json_t* parObj; void loadFile(char* path) { json_error_t error; // 如果parObj之前已经有值,要先释放避免内存泄漏 if (parObj) { json_decref(parObj); } parObj = json_load_file(path, 0, &error); } int getAccountId(char* id) { if (!parObj) { // 处理parObj已被释放或未加载的情况 return -1; // 或者其他错误标识 } json_t* json_acc = json_object_get(parObj, "accounts"); if (!json_acc || !json_is_object(json_acc)) { return -1; } json_t* json_param = json_object_get(json_acc, id); if (!json_param || !json_is_integer(json_param)) { return -1; } return json_integer_value(json_param); } void cleanJson() { if (parObj) { json_decref(parObj); parObj = NULL; // 置空,避免后续误访问已释放的指针 } }
调用侧修正后代码
loadFile("/home/jacob/accountDump.json"); int a_id = getAccountId("10"); cleanJson(); int a_test = getAccountId("10"); // 此时会返回-1,符合预期
这样修改后,cleanJson()会把parObj置空,后续调用getAccountId会先检查parObj是否有效,避免访问已释放的内存。
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

