GArray解引用是否触发unref?相关双重释放问题排查
GLib GArray 双重释放问题排查
可复现代码(修正原代码2处笔误:结构体赋值缺失分号、printf换行符写错)如下:
#include <stdio.h> #include <stdbool.h> #include <stdint.h> #include <glib.h> void transform_pointer_contents(GArray* const arr) { GArray* const tmp = g_array_new(true, true, sizeof(uint8_t)); const uint8_t example_num = 5; g_array_append_val(tmp, example_num); *arr = *tmp; // Why does this cause a double-free error? g_array_unref(tmp); } int main() { GArray* const arr = g_array_new(true, true, sizeof(uint8_t)); transform_pointer_contents(arr); printf("Arr length is %d\n", arr->len); g_array_unref(arr); }
问题解答
1. 为什么执行g_array_unref(tmp)会触发双重释放?
核心原因是*arr = *tmp是纯结构体浅拷贝,没有任何引用计数相关逻辑:
g_array_new()返回的是堆上分配的GArray结构体指针,每个GArray结构体内部持有独立的引用计数字段、指向实际数组存储内存的data指针、数组长度、分配容量等元数据。- 浅拷贝执行后,
arr和tmp指向两个完全独立的GArray结构体实例,但两个实例内部的data指针指向同一块堆内存,且两个实例的引用计数均为初始值1。 - 调用
g_array_unref(tmp)时,tmp的引用计数减为0,GLib会先释放tmp->data指向的数组存储内存,再释放tmp指向的GArray结构体本身。 - 后续main函数执行
g_array_unref(arr)时,arr内部的data指针已经是被释放的野指针,GLib会再次尝试释放这块已经回收的内存,直接触发双重释放错误。 - 额外说明:浅拷贝操作直接覆盖了arr初始化时分配的内部data指针,这部分内存从拷贝完成开始就已经泄漏,无法再被回收。
2. 解引用操作是否会自动触发unref逻辑?
该猜测完全不成立:
- C语言没有运算符重载特性,对指针的解引用、结构体赋值是纯粹的内存字节拷贝操作,不会执行任何额外的自定义逻辑,更不会自动触发GLib的引用计数增减逻辑。
- GLib的引用计数完全是显式实现的:只有主动调用
g_array_ref/g_array_unref时,才会修改对应GArray实例的引用计数字段,在计数归零时执行资源回收。不存在任何隐式的引用计数操作。 - 问题本质是错误的浅拷贝让两个独立的、引用计数互不相关的GArray实例,共同持有同一块data内存的所有权,最终两个实例独立执行回收逻辑时重复释放同一块内存,和隐式unref没有任何关系。
3. 不手动调用g_array_unref(tmp)是否不会内存泄漏?
必然会产生内存泄漏,没有任何例外:
- 第一处泄漏:arr初始化时通过
g_array_new分配的内部data存储内存,被浅拷贝操作覆盖了指针地址,永远无法被回收。 - 第二处泄漏:tmp指向的GArray结构体本身是堆上分配的内存,不调用
g_array_unref(tmp)的话,它的引用计数永远为1,这个结构体实例占用的内存永远不会被释放。 - 即使不unref tmp,最后main函数unref arr的时候,只会释放tmp持有的那块data内存,tmp的结构体本身、arr原来的data内存都处于泄漏状态。
正确实现参考
如果要把tmp的内容转移到arr中,不要做结构体浅拷贝,直接操作数组内容即可:
void transform_pointer_contents(GArray* const arr) { GArray* const tmp = g_array_new(true, true, sizeof(uint8_t)); const uint8_t example_num = 5; g_array_append_val(tmp, example_num); // 按需选择是否先清空arr原有内容,再追加tmp的数据 g_array_append_vals(arr, tmp->data, tmp->len); // 用完tmp正常释放即可,不会有重复释放问题 g_array_unref(tmp); }
内容的提问来源于stack exchange,提问作者karobar
相关产品推荐
相关产品推荐

