【问题标题】:Where is the memory leak when freeing this nested struct?释放这个嵌套结构时内存泄漏在哪里?
【发布时间】:2020-04-15 02:34:38
【问题描述】:

我正在尝试用 C 语言实现 Barnes-Hutt 树,但在释放树时遇到了问题。

树中的节点如下:

struct node {
    int is_external;
    quad q;
    body *b;

    node *nw;
    node *ne;
    node *sw;
    node *se;
};

我用来释放它们的功能是

void free_node(node *n) {
    // free subnodes
    if (n->nw != NULL) {
        free_node(n->nw);
    }
    if (n->ne != NULL) {
        free_node(n->ne);
    }
    if (n->sw != NULL) {
        free_node(n->sw);
    }
    if (n->se != NULL) {
        free_node(n->se);
    }

    // free this node's body
    if (!n->is_external) {
        free(n->b);
    }
    free(n);
}

(我没有释放外部节点的主体,因为它们被保留用于模拟的下一次迭代)。

但是,当我检查内存泄漏时,我得到了

72 bytes in 1 blocks are definitely lost in loss record 24 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002413: insert_in_node (bh_tree.c:165)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 25 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100001E64: insert_in_node (bh_tree.c:108)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 26 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x10000251D: insert_in_node (bh_tree.c:173)
   by 0x100001F97: insert_in_node (bh_tree.c:118)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 27 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002627: insert_in_node (bh_tree.c:181)
   by 0x10000209B: insert_in_node (bh_tree.c:126)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001F97: insert_in_node (bh_tree.c:118)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 28 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x10000206C: insert_in_node (bh_tree.c:124)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x10000209B: insert_in_node (bh_tree.c:126)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001F97: insert_in_node (bh_tree.c:118)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 29 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002170: insert_in_node (bh_tree.c:132)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 30 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002627: insert_in_node (bh_tree.c:181)
   by 0x100001E93: insert_in_node (bh_tree.c:110)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 31 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002627: insert_in_node (bh_tree.c:181)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001E93: insert_in_node (bh_tree.c:110)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 32 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002309: insert_in_node (bh_tree.c:157)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001E93: insert_in_node (bh_tree.c:110)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 33 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x10000206C: insert_in_node (bh_tree.c:124)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001E93: insert_in_node (bh_tree.c:110)
   by 0x100001693: main (sim.c:115)

都指向下面malloc

    node n_sub;
    n_sub.is_external = 1;
    n_sub.q = subquad(n.q, k);
    n_sub.nw = NULL;
    n_sub.ne = NULL;
    n_sub.sw = NULL;
    n_sub.se = NULL;
    n_sub.b = malloc(sizeof(body));
    *n_sub.b = (body) {.id=EMPTY};

    return n_sub;
}

但我相当肯定我正确地释放了所有这些。有什么想法可能会出错吗?

【问题讨论】:

  • 我们需要看到minimal verifiable example。根据定义,您不知道问题出在哪里。因此,通过有选择地显示代码,您可能已经消除了实际的根本原因。因此,请构建一个我们可以自己运行以查看问题的最小示例。 “我没有释放外部节点的主体”:例如,我们如何确保正确完成并且最终释放了这些主体。我们不能,没有所有相关代码。
  • struct body 是如何定义的?它的尺寸是 72 吗?你有没有将is_external 设置为 0?
  • 在释放指针指向的内存后将指针清空是一种很好的做法;这将有助于发现很多错误。
  • 注意:如果n->nw->se == n 迟早会中断。最好将其拆分为一个处理算法的层(可能使用nw 等链接)和一个进行内存分配的层(可能将所有节点保存在一个数组中)。
  • @SteveFriedl 感谢您的提示。我会确保在未来这样做。

标签: c memory-management struct memory-leaks malloc


【解决方案1】:

显然valgrind 总是在抱怨同样的malloc:

at 0x100111CF5: malloc (...)
by 0x100001B95: subnode (bh_tree.c:70)

您在代码中指向的唯一malloc 是subnode 的实现猜测:

node n_sub;
n_sub.is_external = 1;
//...
n_sub.b = malloc(sizeof(body));

但是,在您的 free_node 函数中,如果 is_external 为假,您只会调用 free:

if (!n->is_external) {
    free(n->b);
}

因此,您需要怀疑您认为将 is_external 设置为 false 的任何代码路径并不总是(或者可能永远不会)被调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-22
    • 2010-11-17
    • 2011-01-01
    • 2021-12-14
    • 1970-01-01
    相关资源
    最近更新 更多