我正尝试在C
中编写可操纵GSettings
的应用程序。不幸的是,我遇到了某种内存泄漏,因此我试图对其进行跟踪。我不确定这是库错误还是我遗漏了一些东西。这是我想出的最小的示例,它分配内存直到崩溃。
#include <glib.h>
#include <gio/gio.h>
int main() {
while (1) {
GSettings *settings = g_settings_new("com.linuxmint.updates");
g_object_unref(settings);
//g_clear_object(&settings); // This leaks as well but seems to leak "slower"
}
return 0;
}
任何人都可以解释一下为什么此示例中的内存泄漏以及如何解决该问题吗?
我正在使用libglib-2.0
(2.56.3
/ Ubuntu 18.04 LTS
附带的Mint
版)。
编辑1
根据评论中的请求,我将发布valgrind输出。我正在使用命令:valgrind --tool=memcheck --leak-check=full --leak-resolution=high --num-callers=50 --show-leak-kinds=definite ./main
。我已将程序更改为有限的(循环100.000次)。这是该更改后的参数的输出。
==16375== Memcheck, a memory error detector
==16375== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16375== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16375== Command: ./main
==16375==
==16375==
==16375== HEAP SUMMARY:
==16375== in use at exit: 297,081,397 bytes in 5,056,358 blocks
==16375== total heap usage: 26,147,615 allocs, 21,091,257 frees, 1,064,178,170 bytes allocated
==16375==
==16375== LEAK SUMMARY:
==16375== definitely lost: 0 bytes in 0 blocks
==16375== indirectly lost: 0 bytes in 0 blocks
==16375== possibly lost: 2,840 bytes in 27 blocks
==16375== still reachable: 297,066,261 bytes in 5,056,238 blocks
==16375== of which reachable via heuristic:
==16375== length64 : 1,384 bytes in 28 blocks
==16375== newarray : 1,808 bytes in 33 blocks
==16375== suppressed: 0 bytes in 0 blocks
==16375== Reachable blocks (those to which a pointer was found) are not shown.
==16375== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==16375==
==16375== For counts of detected and suppressed errors, rerun with: -v
==16375== ERROR SUMMARY: 27 errors from 27 contexts (suppressed: 0 from 0)
我不是专家,但是参数still reachable
随着循环次数的增加而增加。如果我一直使用一个变量,如何能够到达那些对象(或者说结构)?我想念什么吗?我正在尝试执行此处建议的操作:https://developer.gnome.org/gobject/stable/gobject-memory.html
编辑2
我正在更深入地研究这个问题。因为不确定我的代码实际上是正确的,所以决定将其更改为另一个GObject
,如下所示:
#include <glib.h>
#include <gio/gio.h>
int main() {
while (1) {
GFile *file = g_file_new_for_path ("/path/to/some/file");
g_object_unref(file);
//g_clear_object(&settings);
}
return 0;
}
我知道这不会打开任何文件,只会创建资源的句柄,但是这段代码会随着时间的推移不断使用内存。如果我删除unref
,则它显然会泄漏并崩溃。
这是valgrind输出查找此代码段100.000和1.000.000迭代的方式。
迭代次数= 100.000
==13257== Memcheck, a memory error detector
==13257== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13257== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==13257== Command: ./main
==13257==
==13257==
==13257== HEAP SUMMARY:
==13257== in use at exit: 159,435 bytes in 1,975 blocks
==13257== total heap usage: 205,209 allocs, 203,234 frees, 6,758,893 bytes allocated
==13257==
==13257== LEAK SUMMARY:
==13257== definitely lost: 0 bytes in 0 blocks
==13257== indirectly lost: 0 bytes in 0 blocks
==13257== possibly lost: 2,528 bytes in 26 blocks
==13257== still reachable: 144,699 bytes in 1,852 blocks
==13257== of which reachable via heuristic:
==13257== length64 : 1,688 bytes in 32 blocks
==13257== newarray : 1,840 bytes in 35 blocks
==13257== suppressed: 0 bytes in 0 blocks
==13257== Rerun with --leak-check=full to see details of leaked memory
==13257==
==13257== For counts of detected and suppressed errors, rerun with: -v
==13257== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
迭代次数= 1.000.000
==12440== Memcheck, a memory error detector
==12440== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12440== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==12440== Command: ./main
==12440==
==12440==
==12440== HEAP SUMMARY:
==12440== in use at exit: 157,241 bytes in 1,936 blocks
==12440== total heap usage: 2,005,339 allocs, 2,003,403 frees, 64,363,746 bytes allocated
==12440==
==12440== LEAK SUMMARY:
==12440== definitely lost: 0 bytes in 0 blocks
==12440== indirectly lost: 0 bytes in 0 blocks
==12440== possibly lost: 2,528 bytes in 26 blocks
==12440== still reachable: 142,505 bytes in 1,813 blocks
==12440== of which reachable via heuristic:
==12440== length64 : 1,688 bytes in 32 blocks
==12440== newarray : 1,840 bytes in 35 blocks
==12440== suppressed: 0 bytes in 0 blocks
==12440== Rerun with --leak-check=full to see details of leaked memory
==12440==
==12440== For counts of detected and suppressed errors, rerun with: -v
==12440== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
这给了我一些想法,即第二个代码的分配与释放的代码几乎相同(两种情况下的差异均小于2000,这可能是库寿命中的一些静态分配)。
在我使用GSettings
对象的第一段中,情况并非如此。分配数量与免费数量之间的关系无处不在,并且会随着时间而增长。
当我可以访问一些滚动发行版(可能是arch)时,我将尝试针对最新的glib
运行该程序,因为我认为编译最新的glib
并将其插入Ubuntu
对我来说太复杂了。
答案 0 :(得分:2)
这种“泄漏”的可访问内存是测试程序的产物。每次构造GSettings
对象时,它都需要向D-Bus会话总线添加一些匹配规则,以便它可以接收来自dconf
守护程序的信号。添加匹配规则意味着将D-Bus方法调用发送到消息总线守护程序,然后等待答复。
通过连续创建100000 GSettings
个对象,您正在排队100000 AddMatch
个对消息总线守护程序的调用,包括100000个分配,这些分配包含有关未决方法调用答复的信息。但是,您的程序在消息总线守护程序回复大多数AddMatch
调用之前退出。因此,许多详细描述待处理回复的分配仍会在退出时分配。
如果您的程序要休眠一分钟,例如,直到消息总线守护程序回复了所有AddMatch
调用,我希望“仍然可以到达”的分配与{{1 }}您运行的示例。
(请注意,可以在GFile
函数中进行usleep()
调用来演示此操作,因为D-Bus方法的调用和答复是在单独的工作线程中处理的。)