简体   繁体   English

将Vala与C结合使用时发生内存泄漏

[英]Getting memory leak when combining Vala with C

The following Vala code combined with C is causing memory leaks and I can't wrap my head around it. 下面的Vala代码与C结合使用会导致内存泄漏,我无法将其缠住。

Main.vala Main.vala

using GLib;

[CCode (cname = "c_function")]
public static extern void c_function (out Tree<int, Tree<int, string>> tree);

public static int main () {

    Tree<int, Tree<int, string>> tree;
    c_function (out tree);
    // If code were to end here and return 0, no memory leak happens, but
    // if we call c_function again, memory leak happens according to valgrind
    c_function (out tree); // Leak happens on this second call
    return 0;
}

main.c main.c中

#include <glib.h>

gint treeCompareFunction (gint a, gint b);

void extern c_function (GTree **tree) {
    *tree = g_tree_new ((GCompareFunc)treeCompareFunction);

    for (int i = 0; i < 3; i++) {
        // Memory leak in the next line when function is called a second time
        GTree * nestedTree = g_tree_new ((GCompareFunc)treeCompareFunction); 
        g_tree_insert (nestedTree, i, "value 1");
        g_tree_insert (*tree, i, (gpointer) nestedTree);
    }
}

gint treeCompareFunction (gint a, gint b) {
    if (a < b) return -1;
    if (a == b) return 0;
    return 1;
}

I don't understand why if I call the C function only once no memory leak happens, but if I call it a second time, line 10 of main.c which creates a Tree in a for loop causes a memory leak. 我不明白为什么只在没有内存泄漏发生时才调用C函数,但是如果我第二次调用它,则main.c的 第10行在 for循环中创建Tree会导致内存泄漏。

The code is compiled with 该代码使用

valac Main.vala main.c -g

And then run with 然后运行

valgrind --leak-check=yes ./Main

I would like to know if it is possible to work around it. 我想知道是否可以解决它。 I tried emptying the tree in the Vala code before calling the C function for the second time. 在第二次调用C函数之前,我尝试清空Vala代码中的树。 No success. 没有成功 Also tried destroying the tree passed as argument if it wasn't NULL on the second call of the C function. 如果第二次调用C函数时它不是NULL,也尝试销毁作为参数传递的树。 No success either. 也没有成功。 Still getting memory leaks. 仍在发生内存泄漏。

Looking at the code you've supplied I would look into using g_tree_new_full () instead of g_tree_new () in your C code. 查看您提供的代码,我将研究在C代码中使用g_tree_new_full ()而不是g_tree_new ()

You are re-using tree as an out argument in the Vala code. 您正在重用tree作为Vala代码中的out参数。 So on the second call the first value assigned to tree should be freed. 因此,在第二次调用时,应该释放分配给tree的第一个值。 I'm hoping Vala generates a call to do that, but I've not written any sample code to check. 我希望Vala生成一个调用来做到这一点,但是我还没有编写任何示例代码来检查。 You can compile your Vala code with the --ccode switch to valac to check the generated C. 您可以使用--ccode开关将valac代码编译为valac以检查生成的C。

So long as Vala is calling g_tree_unref () then it is the set up of your C code that is not freeing the nested tree. 只要Vala调用g_tree_unref () ,您的C代码设置就不会释放嵌套树。 You need a GDestroyNotify function for the nested tree to be passed to g_tree_new_full () . 您需要使用GDestroyNotify函数将嵌套树传递给g_tree_new_full ()

Update 更新

The error is in your C code. 错误在您的C代码中。 Your C code should be: 您的C代码应为:

#include <glib.h>

gint treeCompareFunction (gint a, gint b);

void extern c_function (GTree **tree) {
    *tree = g_tree_new_full ((GCompareFunc)treeCompareFunction,
                             NULL,
                             NULL,
                             g_tree_unref
                             );

    for (int i = 0; i < 3; i++) {
        GTree * nestedTree = g_tree_new ((GCompareFunc)treeCompareFunction);
        g_tree_insert (nestedTree, i, "value 1");
        g_tree_insert (*tree, i, (gpointer) nestedTree);
    }
}

gint treeCompareFunction (gint a, gint b) {
    if (a < b) return -1;
    if (a == b) return 0;
    return 1;
}

Note the use of g_tree_unref as the GDestroyNotify function when using g_tree_new_full . 注意使用的g_tree_unref作为GDestroyNotify当使用功能g_tree_new_full

The Valgrind leak summary now reports: Valgrind泄漏摘要现在报告:

==22035== LEAK SUMMARY:
==22035==    definitely lost: 0 bytes in 0 blocks
==22035==    indirectly lost: 0 bytes in 0 blocks
==22035==      possibly lost: 1,352 bytes in 18 blocks

Before, with the code in your question, the leak summary was: 之前,使用您的问题中的代码,泄漏摘要为:

==21436== LEAK SUMMARY:
==21436==    definitely lost: 288 bytes in 6 blocks
==21436==    indirectly lost: 240 bytes in 6 blocks
==21436==      possibly lost: 1,352 bytes in 18 blocks

Found the solution. 找到了解决方案。 It was by no means trivial as it required looking at what Vala was doing and looking at the gtree.c source code to understand what was happening to the allocated memory of the trees. 这绝对不是一件容易的事,因为它需要查看Vala在做什么,并查看gtree.c源代码以了解正在分配给树的内存的情况。

Because Vala calls g_tree_unref on the root tree by default at the end of the program, the root tree is freed, but the memory chunks of the nested trees that were part of it are lost and not freed. 由于Vala在程序末尾默认情况下在根树上调用g_tree_unref ,因此将释放根树,但作为它一部分的嵌套树的内存块将丢失且不会被释放。 One has to make Vala call g_tree_unref on those nested trees. 必须让Vala在这些嵌套树上调用g_tree_unref A way to go about it is by owning the references to the nested trees. 一种解决方法是拥有对嵌套树的引用。 That can be done in the foreach TraverseFunc of the root tree in the following manner 可以通过以下方式在根树的foreach TraverseFunc中完成此操作

Main.vala Main.vala

using GLib;

[CCode (cname = "c_function")]
public static extern void c_function (out Tree<int, Tree<int, string>> tree);

public static int main () {
    Tree<int, Tree<int, string>> tree;
    c_function (out tree);
    // Iterate through the tree and get a strong reference to the values
    // to free them
    tree.@foreach ((TraverseFunc<int, Tree<int, string>>)valueDestroyThroughTraversing);
    c_function (out tree);
    tree.@foreach ((TraverseFunc<int, Tree<int, string>>)valueDestroyThroughTraversing);
    return 0;
}

public bool valueDestroyThroughTraversing (int treeKey, owned Tree<int, string> treeValue) {
    // Do something with the keys and values of the tree if desired
    // treeValue will go out of scope at the end of the method 
    // and Vala will free it
    return false;
}

main.c main.c中

#include <stdio.h>
#include <glib.h>

gint treeCompareFunction (gint a, gint b);

void extern c_function (GTree **tree) {
    *tree = g_tree_new ((GCompareFunc)treeCompareFunction);

    for (int i = 0; i < 3; i++) {
        GTree * nestedTree = g_tree_new ((GCompareFunc)treeCompareFunction); 
        g_tree_insert (nestedTree, (gpointer) ((gintptr)i), "value 1");
        g_tree_insert (*tree, (gpointer) ((gintptr)i), nestedTree);
    }
}

gint treeCompareFunction (gint a, gint b) {
    if (a < b) return -1;
    if (a == b) return 0;
    return 1;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM