Re: Glib hashtable memory leak



Hello all,

Thanks for the feedback, and I will summarize the "howto" on this. I believe the same memory leakage reporting issue is being discussed in the thread "checking for memory leaks in gtk". I am ok with Glib not explicitly freeing some singletons on exit, but the real issue is to distinguish genuine leaks in user code from "leaks" that are attributable to Glib.

First, the problem. When the `G_' macros are defined, less memory is reported as leaked, but some still remains unaccounted for according to mtrace. See the comparison below:

$ cat test1.c
#include <glib.h>
#include <mcheck.h>

int main()
{
        mtrace();

        GHashTable* hash_table;

        hash_table=g_hash_table_new(g_str_hash, g_str_equal);
        g_hash_table_unref (hash_table);

        return 1;
}


$ gcc test1.c -o test1 -I/usr/include/glib-2.0/ -I/usr/lib64/glib-2.0/include/ -lglib-2.0
$ export MALLOC_TRACE=0.mtrace.log
$ ./test1; mtrace ./test1 $MALLOC_TRACE

Memory not freed:
-----------------
           Address     Size     Caller
0x0000000000601460    0x1f8  at 0x3fea834002
0x0000000000601660     0xfc  at 0x3fea834002
0x0000000000601770    0x1f8  at 0x3fea834002
0x0000000000601970    0x7f0  at 0x3fea834002
0x0000000000602400    0x3f0  at 0x3fea8423e1
0x0000000000602800    0x3f0  at 0x3fea8423e1

Now, when the G_ macros are defined, less memory is reported as leaked.

$ export G_DEBUG=gc-friendly
$ export G_SLICE=always-malloc
$ ./test1; mtrace ./test1 $MALLOC_TRACE

Memory not freed:
-----------------
           Address     Size     Caller
0x0000000000601460    0x1f8  at 0x3fea834002
0x0000000000601660     0xfc  at 0x3fea834002
0x0000000000601770    0x1f8  at 0x3fea834002

Simple cases such as:

        malloc(20);
        GHashTable* hash_table;
        hash_table=g_hash_table_new(g_str_hash, g_str_equal);
        g_hash_table_unref (hash_table);

can be easily caught by adding the -g option when compiling:

$ gcc -g test1.c -o test1 -I/usr/include/glib-2.0/ -I/usr/lib64/glib-2.0/include/ -lglib-2.0
$ ./test1; mtrace ./test1 $MALLOC_TRACE

Memory not freed:
-----------------
           Address     Size     Caller
0x0000000000601460     0x14  at /home/user/devel/linux/glib/test1.c:13 <-------- obviously, your code does not free allocated memory
0x0000000000601480    0x1f8  at 0x3fea834002
0x0000000000601680     0xfc  at 0x3fea834002
0x0000000000601790    0x1f8  at 0x3fea834002

But when leaks are related to not using glib properly, such as not calling unref, things are more difficult to diagnose:

        GHashTable* hash_table;
        hash_table=g_hash_table_new(g_str_hash, g_str_equal);

$ gcc -g test1.c -o test1 -I/usr/include/glib-2.0/ -I/usr/lib64/glib-2.0/include/ -lglib-2.0
$ ./test1; mtrace ./test1 $MALLOC_TRACE

Memory not freed:
-----------------
           Address     Size     Caller
0x0000000000601460    0x1f8  at 0x3fea834002
0x0000000000601660     0xfc  at 0x3fea834002
0x0000000000601770    0x1f8  at 0x3fea834002
0x0000000000601970     0x38  at 0x3fea83407b
0x00000000006019b0     0x58  at 0x3fea834002 <----------- Is there an memory allocation error? If yes, where and why?

With valgrind, the diagnostics is much better though (note the --leak-check=full option for valgrind and the -g option for gcc):

$ G_SLICE=always-malloc G_DEBUG=gc-friendly     valgrind --tool=memcheck --leak-check=full ./test1
--- some lines cut out
==4071== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 1)
==4071== malloc/free: in use at exit: 2,484 bytes in 7 blocks.
==4071== malloc/free: 7 allocs, 0 frees, 2,484 bytes allocated.
==4071== For counts of detected errors, rerun with: -v
==4071== searching for pointers to 7 not-freed blocks.
==4071== checked 74,704 bytes.
==4071==
==4071== 144 (56 direct, 88 indirect) bytes in 1 blocks are definitely lost in loss record 1 of 5
==4071==    at 0x4A05996: malloc (vg_replace_malloc.c:149)
==4071==    by 0x3FEA83407A: g_malloc (in /lib64/libglib-2.0.so.0.1200.13)
==4071==    by 0x3FEA820CF2: g_hash_table_new_full (in /lib64/libglib-2.0.so.0.1200.13)
==4071==    by 0x4005E3: main (test1.c:13) <----------------------------------------------------------- AHA! there's the culprit
--- some lines cut out

And when the leak is fixed (add an unref call to the code above), the valgrind output looks better:

==4105== LEAK SUMMARY:
==4105==    definitely lost: 0 bytes in 0 blocks.
==4105==      possibly lost: 0 bytes in 0 blocks.
==4105==    still reachable: 2,340 bytes in 5 blocks.
==4105==         suppressed: 0 bytes in 0 blocks.

So I think this pretty much solves my issue of checking for memory leaks with glib, many thanks for your help.

Regards,
Ovidiu


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]