Memory leak when unref MMManager object

Aleksander Morgado aleksander at aleksander.es
Fri Mar 13 16:43:07 UTC 2020


Hey Adrien,

>
> I need your help to resolve a memory leak problem. I make some tests with a little and simple software:
>
> int main(int argc, char* argv[])
> {
>
>     MMManager *manager;
>     GError *error = NULL;
>     GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
>
>     while(1){
>
>         manager = mm_manager_new_sync(connection,
>                                       G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
>                                       NULL, &error);
>         g_object_unref(manager);
>         sleep(1);
>     }
> }
>
> After each loop the memory usage is increased by 4 kB, the object manager doest not seem to be free.
>
> This problem with g_object_unref() appears only with objects with MMManager type.
>

I modified your tester to add an additional unref() for the
GDBusConnection, as that wasn't getting disposed, but that still
wasn't enough. Here's what valgrind finds on your tester after I added
the extra GDBusConnection unref:

==43435== 31,680 bytes in 990 blocks are still reachable in loss
record 2,179 of 2,182
==43435==    at 0x483977F: malloc (vg_replace_malloc.c:309)
==43435==    by 0x4C02FA8: g_malloc (gmem.c:102)
==43435==    by 0x4BFAE3F: g_source_set_callback (gmain.c:1758)
==43435==    by 0x4A7A311: call_destroy_notify.part.0 (gdbusconnection.c:266)
==43435==    by 0x4A7A5E2: call_destroy_notify (gdbusconnection.c:257)
==43435==    by 0x4A7A5E2: signal_subscriber_unref (gdbusconnection.c:3286)
==43435==    by 0x4A7A5E2: signal_subscriber_unref (gdbusconnection.c:3278)
==43435==    by 0x4BCC705: ptr_array_remove_index (garray.c:1545)
==43435==    by 0x4A7DF9E: unsubscribe_id_internal (gdbusconnection.c:3645)
==43435==    by 0x4A8225A: g_dbus_connection_signal_unsubscribe
(gdbusconnection.c:3717)
==43435==    by 0x4A8CDCC: g_dbus_proxy_finalize (gdbusproxy.c:189)
==43435==    by 0x4B6727D: g_object_unref (gobject.c:3499)
==43435==    by 0x4B6727D: g_object_unref (gobject.c:3391)
==43435==    by 0x4BE9D61: g_hash_table_remove_all_nodes.part.0 (ghash.c:706)
==43435==    by 0x4BEB10E: g_hash_table_remove_all_nodes (ghash.c:628)
==43435==    by 0x4BEB10E: g_hash_table_unref (ghash.c:1461)
==43435==
==43435== 38,610 bytes in 990 blocks are still reachable in loss
record 2,180 of 2,182
==43435==    at 0x483977F: malloc (vg_replace_malloc.c:309)
==43435==    by 0x4C02FA8: g_malloc (gmem.c:102)
==43435==    by 0x4C1CD5F: g_strdup (gstrfuncs.c:363)
==43435==    by 0x4BFB490: g_source_set_name (gmain.c:2082)
==43435==    by 0x4A7A320: call_destroy_notify.part.0 (gdbusconnection.c:270)
==43435==    by 0x4A7A5E2: call_destroy_notify (gdbusconnection.c:257)
==43435==    by 0x4A7A5E2: signal_subscriber_unref (gdbusconnection.c:3286)
==43435==    by 0x4A7A5E2: signal_subscriber_unref (gdbusconnection.c:3278)
==43435==    by 0x4BCC705: ptr_array_remove_index (garray.c:1545)
==43435==    by 0x4A7DF9E: unsubscribe_id_internal (gdbusconnection.c:3645)
==43435==    by 0x4A8225A: g_dbus_connection_signal_unsubscribe
(gdbusconnection.c:3717)
==43435==    by 0x4A8CDCC: g_dbus_proxy_finalize (gdbusproxy.c:189)
==43435==    by 0x4B6727D: g_object_unref (gobject.c:3499)
==43435==    by 0x4B6727D: g_object_unref (gobject.c:3391)
==43435==    by 0x4BE9D61: g_hash_table_remove_all_nodes.part.0 (ghash.c:706)
==43435==
==43435== 43,560 bytes in 1,089 blocks are still reachable in loss
record 2,181 of 2,182
==43435==    at 0x483977F: malloc (vg_replace_malloc.c:309)
==43435==    by 0x4C02FA8: g_malloc (gmem.c:102)
==43435==    by 0x4C1AF21: g_slice_alloc (gslice.c:1025)
==43435==    by 0x4C1B549: g_slice_alloc0 (gslice.c:1051)
==43435==    by 0x4BFA73B: g_source_new (gmain.c:945)
==43435==    by 0x4BFE322: g_idle_source_new (gmain.c:5780)
==43435==    by 0x4A7A2EB: call_destroy_notify.part.0 (gdbusconnection.c:264)
==43435==    by 0x4A7A5E2: call_destroy_notify (gdbusconnection.c:257)
==43435==    by 0x4A7A5E2: signal_subscriber_unref (gdbusconnection.c:3286)
==43435==    by 0x4A7A5E2: signal_subscriber_unref (gdbusconnection.c:3278)
==43435==    by 0x4BCC705: ptr_array_remove_index (garray.c:1545)
==43435==    by 0x4A7DF9E: unsubscribe_id_internal (gdbusconnection.c:3645)
==43435==    by 0x4A8225A: g_dbus_connection_signal_unsubscribe
(gdbusconnection.c:3717)
==43435==    by 0x4A8CDCC: g_dbus_proxy_finalize (gdbusproxy.c:189)
==43435==
==43435== 95,040 bytes in 990 blocks are still reachable in loss
record 2,182 of 2,182
==43435==    at 0x483BB65: calloc (vg_replace_malloc.c:762)
==43435==    by 0x4C03000: g_malloc0 (gmem.c:132)
==43435==    by 0x4BFA72E: g_source_new (gmain.c:944)
==43435==    by 0x4BFE322: g_idle_source_new (gmain.c:5780)
==43435==    by 0x4A7A2EB: call_destroy_notify.part.0 (gdbusconnection.c:264)
==43435==    by 0x4A7A5E2: call_destroy_notify (gdbusconnection.c:257)
==43435==    by 0x4A7A5E2: signal_subscriber_unref (gdbusconnection.c:3286)
==43435==    by 0x4A7A5E2: signal_subscriber_unref (gdbusconnection.c:3278)
==43435==    by 0x4BCC705: ptr_array_remove_index (garray.c:1545)
==43435==    by 0x4A7DF9E: unsubscribe_id_internal (gdbusconnection.c:3645)
==43435==    by 0x4A8225A: g_dbus_connection_signal_unsubscribe
(gdbusconnection.c:3717)
==43435==    by 0x4A8CDCC: g_dbus_proxy_finalize (gdbusproxy.c:189)
==43435==    by 0x4B6727D: g_object_unref (gobject.c:3499)
==43435==    by 0x4B6727D: g_object_unref (gobject.c:3391)
==43435==    by 0x4BE9D61: g_hash_table_remove_all_nodes.part.0 (ghash.c:706)
==43435==
==43435== LEAK SUMMARY:
==43435==    definitely lost: 0 bytes in 0 blocks
==43435==    indirectly lost: 0 bytes in 0 blocks
==43435==      possibly lost: 2,664 bytes in 27 blocks
==43435==    still reachable: 515,070 bytes in 9,835 blocks
==43435==                       of which reachable via heuristic:
==43435==                         length64           : 4,944 bytes in 57 blocks
==43435==                         newarray           : 1,936 bytes in 41 blocks
==43435==         suppressed: 0 bytes in 0 blocks

There are no "memory leaks" per se; instead, there is memory that is
allocated but it is still reachable at program exit. The blocks
reported above are due to idle sources scheduled in the default
GMainContext, looks like there is some "cleanup in idle" logic
happening in the GDBusConnection object. That allocated memory is
triggered by creating the MMManager, and once the manager is unref-ed,
one "cleanup in idle" action is queued in the GDBusConnection,
something like that. So if you create 100 MMManagers and unref them
right away, all of those will end up scheduling the "cleanup in idle"
actions in the GDBusConnection.

I assume your program doesn't have a GMainLoop? I'm not totally sure
why those cleanups are scheduled in idle (instead of just running the
cleanup without scheduling anything), I'd need to dig in the GLib
code. I did a quick hack just setting up a quick main loop with 1s
timeout before exit (see attached), and all that memory gets correctly
freed before exit. Not saying you should do that, that was just a test
to validate assumptions.

Just using the "sync" APIs in libmm-glib and creating multiple
MMManager objects just to dispose them should be a supported thing,
but why is your program creating multiple MMManager objects anyway?
Maybe we can find another way to solve this issue if you're not
planning in setting up a GLib main loop in your code.

-- 
Aleksander
https://aleksander.es
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test.c
Type: text/x-csrc
Size: 829 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/modemmanager-devel/attachments/20200313/95dfccdc/attachment.c>


More information about the ModemManager-devel mailing list