调用GLib中g_bus_get_sync为何出现内存泄漏?
g_bus_get_sync 检测到疑似内存泄漏,是否是我的操作有误?
我编写了以下代码,编译后用Valgrind检测,发现g_bus_get_sync似乎存在内存泄漏,请问是我哪里操作错了吗?
#include <gio/gio.h> #include <libmm-glib.h> static gboolean loop_ready(GMainLoop *loop) { g_main_loop_quit(loop); return G_SOURCE_REMOVE; } int main(int argc, char *argv[]) { g_autoptr(GDBusConnection) connection = NULL; g_autoptr(GError) error = NULL; connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); if (!connection) { g_printerr("Error: %s\n", error->message); } // EDIT: Added g_dbus_connection_close_sync at suggestion of @user7860670 g_dbus_connection_close_sync(connection); g_clear_object(&connection); { g_autoptr(GMainLoop) loop = NULL; loop = g_main_loop_new(NULL, FALSE); g_idle_add((GSourceFunc)loop_ready, loop); g_main_loop_run(loop); } }
Valgrind命令:
valgrind --tool=memcheck --suppressions=/usr/share/glib-2.0/valgrind/glib.supp --leak-check=full ./memtest
Valgrind检测结果:
==178041== Memcheck, a memory error detector ==178041== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==178041== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==178041== Command: ./memtest ==178041== ==178041== ==178041== HEAP SUMMARY: ==178041== in use at exit: 104,240 bytes in 1,190 blocks ==178041== total heap usage: 2,299 allocs, 1,109 frees, 238,311 bytes allocated ==178041== ==178041== 304 bytes in 1 blocks are possibly lost in loss record 946 of 969 ==178041== at 0x484DA83: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==178041== by 0x40147D9: calloc (rtld-malloc.h:44) ==178041== by 0x40147D9: allocate_dtv (dl-tls.c:375) ==178041== by 0x40147D9: _dl_allocate_tls (dl-tls.c:634) ==178041== by 0x4EC4834: allocate_stack (allocatestack.c:430) ==178041== by 0x4EC4834: pthread_create@@GLIBC_2.34 (pthread_create.c:647) ==178041== by 0x4B53504: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4) ==178041== by 0x4B2ECCC: g_thread_new (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4) ==178041== by 0x4AFF616: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4) ==178041== by 0x49207D7: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.7200.4) ==178041== by 0x4920884: g_task_get_type (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.7200.4) ==178041== by 0x498C288: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.7200.4) ==178041== by 0x4980020: g_bus_get_sync (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.7200.4) ==178041== by 0x1093AE: main (memtest.cpp:15) ==178041== ==178041== 304 bytes in 1 blocks are possibly lost in loss record 947 of 969 ==178041== at 0x484DA83: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==178041== by 0x40147D9: calloc (rtld-malloc.h:44) ==178041== by 0x40147D9: allocate_dtv (dl-tls.c:375) ==178041== by 0x40147D9: _dl_allocate_tls (dl-tls.c:634) ==178041== by 0x4EC4834: allocate_stack (allocatestack.c:430) ==178041== by 0x4EC4834: pthread_create@@GLIBC_2.34 (pthread_create.c:647) ==178041== by 0x4B53504: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4) ==178041== by 0x4B2ECCC: g_thread_new (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4) ==178041== by 0x49762D9: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.7200.4) ==178041== by 0x497FFF3: g_bus_get_sync (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.7200.4) ==178041== by 0x1093AE: main (memtest.cpp:15) ==178041== ==178041== LEAK SUMMARY: ==178041== definitely lost: 0 bytes in 0 blocks ==178041== indirectly lost: 0 bytes in 0 blocks ==178041== possibly lost: 608 bytes in 2 blocks ==178041== still reachable: 64,442 bytes in 533 blocks ==178041== suppressed: 26,374 bytes in 533 blocks ==178041== Reachable blocks (those to which a pointer was found) are not shown. ==178041== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==178041== ==178041== For lists of detected and suppressed errors, rerun with: -s
解答
从Valgrind的栈回溯可以看出,这些“疑似泄漏”的内存并非g_bus_get_sync本身的问题,而是GIO内部创建的后台线程的TLS(线程本地存储)资源。具体原因和解决方式如下:
泄漏本质分析
- 标记为“possibly lost”的内存是
pthread_create时分配的线程本地存储结构,这类内存在线程正常退出时会由操作系统自动回收,但Valgrind无法跟踪到库内部线程的释放路径,因此误标记为疑似泄漏。 - 你的代码本身没有错误:
g_autoptr已经正确管理了GDBusConnection的生命周期,手动调用g_dbus_connection_close_sync和g_clear_object也没问题,但GLib内部的后台线程需要时间完成清理。
- 标记为“possibly lost”的内存是
消除Valgrind报告的方法
- 方法一:让GLib完成线程清理
在释放连接后,添加主循环迭代,给后台线程处理收尾工作的时间:g_clear_object(&connection); // 处理所有pending事件,确保后台线程退出 while (g_main_context_iteration(NULL, FALSE)); - 方法二:添加抑制规则
如果确认这些是假阳性报告,可以在Valgrind的抑制文件中添加规则,忽略这类线程TLS相关的泄漏:{ name: glib-thread-tls Memcheck:Leak match-leak-kinds: possibly fun: calloc fun: allocate_dtv fun: _dl_allocate_tls fun: allocate_stack fun: pthread_create@@GLIBC_2.34 fun: g_thread_new } - 方法三:忽略假阳性
程序退出时操作系统会回收所有进程内存,这类“possibly lost”的内存不会造成实际的内存泄漏问题,无需刻意处理。
- 方法一:让GLib完成线程清理
额外建议
- 不需要手动调用
g_clear_object,g_autoptr会在变量作用域结束时自动释放对象; g_bus_get_sync返回的是共享的DBus连接,GLib会缓存该连接,即使你关闭自己的引用,缓存中的连接可能仍会保留到程序退出,这是正常行为。
- 不需要手动调用
内容的提问来源于stack exchange,提问作者Puddler
相关产品推荐
相关产品推荐

