Locate and remove memory leaks in Javascript

If I want to avoid memory leaks when using GObject and Javascript, is it enough to disconnect signals that use the arrow function in the callback to ensure that all the memory inside is freed?

Also, if I call “destroy()”, is it enough to ensure that all the memory is freed and no leaks are possible, even if I don’t disconnect the signals?

destroy() implementations are typically just functions that emit a signal before calling GObject.Object.run_dispose(), so there’s nothing particularly special about them. Part of the default dispose method is GObject.signal_handlers_destroy(), so these are disconnected when an object drops its last reference.

The best way to “free” objects in GJS is just to ensure that an object can’t be traced to a variable. GJS should already be able to handle cyclic references, as long as the “top” of the tree can not be traced.

If you want to ensure that is definitely happening, or figure out why it is not, there are some tools in the GJS repository like heapgraph.py that can help.

1 Like

I know the theory; but according to Tips On Memory Management | GNOME JavaScript , there can be some cases where there are no variables that point to an object, but there can be references to it. That’s my worry.

What that refers to is situations like below:

function startTimer() {
    const sourceId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
        return GLib.SOURCE_CONTINUE;
    });
}

When startTimer() returns, the variable sourceId will be collected, but the GLib.MainContext will still hold a reference to the GLib.Source structure. GJS will also free the JS wrapper, if one was created at all, but the C structure will remain.

These are plain old memory leaks (sometimes called reference leaks), which aren’t related to JavaScript or GJS. Cases like this will have to be debugged with AddressSanitizer or Valgrind, since it won’t appear in a heap dump from the JavaScript engine.

The same leak in C:

static gboolean
source_cb (gpointer user_data)
{
  return G_SOURCE_CONTINUE;
}

static void
start_timer (void)
{
  unsigned int source_id = 0;

  source_id = g_timeout_add (1000, (GSourceFunc)source_cb, NULL);
}

I used heapgraph, and it seems to be a leak with a GOBJECT_BOXED type… so I suspect that it’s something that needs to be manually destroyed.

Interesting, do you know what the GType is? Boxed types typically have to register public ref/unref functions, so those should be handled automatically by GJS.

I’m trying to find it… This is all what heapgraph returns:

╰─[fp argv[0]]─➤ [GObject_Union <unknown object> jsobj@0x1f2533917ce0]

And another…

╰─[baseline-args[0]]─➤ [GObject_Union <unknown object> jsobj@0x273226418b00]

There aren’t a lot of union's in GObject, have you run this application with Valgrind?

It’s also possible to run the application with gdb, send it SIGUSR1 to dump the heap, grab the address and get information about it that way. There’s some details about this kind of debugging here.

This is odd… In some cases I’m unable to reproduce that… Anyway, there seems to be some kind of memory leak, because every time that I refresh the desktop, the memory used by desktop icons increases… Sometimes it returns to the original value after 10 seconds, but other times it doesn’t…

I think your first step is to confirm you actually have a memory leak. Running the application with Valgrind is probably the most straight-forward way to do that.

Increases in memory, or memory that hangs around for a non-deterministic amount of time doesn’t necessarily mean there’s a leak. JavaScript engines routinely allocate large chunks of memory, then “sub-allocate” them to JavaScript code.

Without running the application and letting it exit cleanly, you’ll have a hard time determining if/where there’s a leak. If the JavaScript engine isn’t receiving some memory pressure, it may decide de-allocating memory it might later re-use is unnecessary.

I’ll try it… Thanks!

Ok… using Valgrind there seems to be a very, very small leak (about a fistful of bytes per refresh), but nothing really important… So it seems that there is really no leak…

Sorry for the false alarm.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.