See if toggled-up objects can be traced from the global object
Currently GjsMaybeOwned (in jsapi-util-root.h) stores pointers to objects or other "GC things" of type T
that can be in one of three modes:
- rooted, that is, never garbage collected
- traced, that is, prevented from garbage collection by another object
- weak, that is, eligible for garbage collection, and the pointer gets nulled out when that happens
The actual part of the data structure that stores this, is a union plus a bitfield:
union {
JS::PersistentRooted<T>* root; // for mode 1
JS::Heap<T> heap; // for mode 2 & 3
} m_thing;
bool m_rooted : 1;
This is in total 16 bytes on x86_64 (8 for the pointer, overlaid with 8 for the Heap object which is supposed to be binary compatible with a pointer, and a wasteful 8 for the bitfield.)
Mostly mode 1 is used for toggled-up GObject wrappers, although it is also used for closures. When the GjsMaybeOwned wrapper switches to mode 1, a JS::PersistentRooted
is allocated, which is 32 bytes.
Instead of rooting the objects in mode 1, it may be possible to trace them from the GjsContext or the GjsGlobal object, which would have the same effect. That could save space, since then we could get rid of the union and always use a JS::Heap
.
In addition, it's my understanding that "special" objects like GObjects are never allocated in the nursery, so it should be possible to use JS::TenuredHeap
instead of JS::Heap
. Objects outside of the nursery are allocated at certain alignment boundaries, so their addresses are always a multiple of 8, and JS::TenuredHeap
allows using the lower bits for flags, which would allow us to get rid of the wasteful bitfield.
However, if we were to trace the objects in mode 1 from the global object, we'd have to keep a list of them somewhere. For this to be an improvement, it shouldn't be the case that the list of objects to trace takes up more bytes per object than the JS::PersistentRooted
already did.