Hello all,
I know this is a bit of a niche topic, but I’ll describe the phenomenon I just found while fooling around with both garbage collection systems. I don’t think this is a bug per se, but I do think it should either be documented somewhere or be treated as a bug (see last paragraph for a possible desired effect with minimal memory cost).
A minimal working example that explains the situation is the following one (gjs binary in /usr/bin):
#!/usr/bin/gjs -m
import Adw from 'gi://Adw'
// This registry is just for detecting garbage collection
const registry = new FinalizationRegistry(console.log)
const app = new Adw.Application()
app.connect('activate', (app)=>{
// I use a dialog here just because it can be closed by pressing 'Esc'.
const dialog = new Adw.Dialog({
child: new Adw.Bin(),
})
// This will log a message after the bin is garbage collected
registry.register(dialog.child, 'The bin has been garbage collected.')
// This is for the script to run while the dialog is open
app.hold()
dialog.connect('closed', (dialog)=>{
app.release()
})
dialog.present(null)
// The next line is for entering the event loop frequently
setInterval(()=>{}, 100)
})
await app.runAsync(['gjs-test'])
Upon running the script, after about 10 seconds, the Adw.Bin dialog.child gets garbage collected from the gjs side, as noted by the log. This is probably done for performance reasons (it would probably consume too much memory to have gjs objects for each widget in the hierarchy), so I just want to make it clear that this is not a complaint.
On the other hand, if one were to wish for that specific bin not to be garbage collected from the gjs side, the previous behaviour can be easily sidesteped by adding a javascript attribute to the object, like dialog.child._ = ''. This will still allow for the Adw.Bin to be garbage collected after, there are no other GObject nor gjs references.
For some reason, the dialog itself does not seem to be garbage collected in my experiments (maybe because it is been presented).
The main reason this should be more explicitly documented is because some people (at least, I do this in regular javascript) may use FinalizationRegistry for cleaning up references, or WeakMap instances to add weak references.
Another possibility would be to treat this as a bug. Of course, gjs objects that are instances of GObject.Object should still be garbage collected when possible for performance reasons, but it should be avoided if it has references from the GObject side; and either it is currently in a FinalizationRegistry, it is a key of a WeakMap, or it has a WeakRef reference. This could copy the behaviour observed when it has a javascript attribute.
Thank you for your attention, if you have read so far.
P.S.: By the way, what is the behaviour in other languages with native garbage collecion?