将 Vala 与 C 结合使用时出现内存泄漏
Getting memory leak when combining Vala with C
以下 Vala 代码与 C 结合导致内存泄漏,我无法理解它。
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
#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;
}
我不明白为什么如果我只调用一次 C 函数就不会发生内存泄漏,但是如果我第二次调用它,line 10 of main.c 在 for 循环中创建树会导致内存泄漏。
代码用
编译
valac Main.vala main.c -g
然后 运行 和
valgrind --leak-check=yes ./Main
我想知道是否可以解决这个问题。在第二次调用 C 函数之前,我尝试在 Vala 代码中清空树。没有成功。如果在第二次调用 C 函数时它不是 NULL,还尝试销毁作为参数传递的树。也没有成功。仍有内存泄漏。
查看您提供的代码,我会考虑在您的 C 代码中使用 g_tree_new_full ()
而不是 g_tree_new ()
。
您正在重新使用 tree
作为 Vala 代码中的输出参数。所以在第二次调用时,分配给 tree
的第一个值应该被释放。我希望 Vala 生成一个调用来执行此操作,但我没有编写任何示例代码来检查。您可以使用 --ccode
切换到 valac
来编译 Vala 代码以检查生成的 C.
只要 Vala 正在调用 g_tree_unref ()
,那么就是 C 代码的设置没有释放嵌套树。您需要 GDestroyNotify
函数才能将嵌套树传递给 g_tree_new_full ()
.
更新
错误在您的 C 代码中。你的 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;
}
注意在使用g_tree_new_full
时将g_tree_unref
作为GDestroyNotify
函数使用。
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
之前,使用您问题中的代码,泄漏摘要是:
==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
找到解决方案。这绝不是微不足道的,因为它需要查看 Vala 在做什么并查看 gtree.c 源代码以了解树的分配内存发生了什么。
因为 Vala 默认在程序结束时调用根树 g_tree_unref
,根树被释放,但是嵌套树的内存块丢失并且没有被释放.必须在那些嵌套树上调用 Vala g_tree_unref
。一种解决方法是拥有对嵌套树的引用。这可以通过以下方式在根树的 foreach TraverseFunc
中完成
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
#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;
}
以下 Vala 代码与 C 结合导致内存泄漏,我无法理解它。
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
#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;
}
我不明白为什么如果我只调用一次 C 函数就不会发生内存泄漏,但是如果我第二次调用它,line 10 of main.c 在 for 循环中创建树会导致内存泄漏。
代码用
编译valac Main.vala main.c -g
然后 运行 和
valgrind --leak-check=yes ./Main
我想知道是否可以解决这个问题。在第二次调用 C 函数之前,我尝试在 Vala 代码中清空树。没有成功。如果在第二次调用 C 函数时它不是 NULL,还尝试销毁作为参数传递的树。也没有成功。仍有内存泄漏。
查看您提供的代码,我会考虑在您的 C 代码中使用 g_tree_new_full ()
而不是 g_tree_new ()
。
您正在重新使用 tree
作为 Vala 代码中的输出参数。所以在第二次调用时,分配给 tree
的第一个值应该被释放。我希望 Vala 生成一个调用来执行此操作,但我没有编写任何示例代码来检查。您可以使用 --ccode
切换到 valac
来编译 Vala 代码以检查生成的 C.
只要 Vala 正在调用 g_tree_unref ()
,那么就是 C 代码的设置没有释放嵌套树。您需要 GDestroyNotify
函数才能将嵌套树传递给 g_tree_new_full ()
.
更新
错误在您的 C 代码中。你的 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;
}
注意在使用g_tree_new_full
时将g_tree_unref
作为GDestroyNotify
函数使用。
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
之前,使用您问题中的代码,泄漏摘要是:
==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
找到解决方案。这绝不是微不足道的,因为它需要查看 Vala 在做什么并查看 gtree.c 源代码以了解树的分配内存发生了什么。
因为 Vala 默认在程序结束时调用根树 g_tree_unref
,根树被释放,但是嵌套树的内存块丢失并且没有被释放.必须在那些嵌套树上调用 Vala g_tree_unref
。一种解决方法是拥有对嵌套树的引用。这可以通过以下方式在根树的 foreach TraverseFunc
中完成
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
#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;
}