You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:18:24