consider making cycle collector traverse fewer JS objects




10 years ago
7 years ago


(Reporter: dbaron, Unassigned)




Firefox Tracking Flags

(Not tracked)


I've been thinking a little bit about how to reduce long GC pauses from the cycle collector.  These are most problematic when there are lots of windows open.  In such cases, I suspect that many of those windows haven't had any JS executing in them since the previous cycle collection.  This may, however, not be the case, due to timers and such.  If it's not the case, the optimizations described here probably would not be effective.  We should measure that (and other things I mention below) before attempting to do any of these things, which I'm really not sure would be worthwhile.

Right now we make NoteRoot/NoteXPCOMRoot calls for lots of XPConnect wrappers to compensate for our inability to track which JS objects are purple according to the cycle collection algorithm.  (In theory, we need to do this for all objects at one direction of the boundary: i.e., all objects where C++ references JS, or all objects where JS references C++, since the point is to make sure we note one object in any potential cycle that involves both C++ and JS in which the only objects that would (according to the original cycle collection algorithm) have become purple since the last cycle collection are in JS.  I think right now we may be doing it for wrappers in both directions, although I'm not sure.)  This noting means that we basically traverse all JS objects in every cycle collection.

It *might* be a worthwhile performance optimization to avoid doing this in cases where we know that noting the wrappers doesn't matter, because any potential cycle through those wrappers was, at the last cycle collection, kept alive by unaccounted references on the C++ side.  This means that for that cycle to become collectable, an object on the C++ side would have to go in the purple buffer.

A prerequisite to doing this effectively would probably be to mark the wrappers only in the JS-referencing-C++ direction, although it might work well even with our current approach as long as the things I describe as wrappers in the algorithm below describe everything that we call NoteRoot on.

The basic idea is that we could maintain a set of wrappers that do not need to be noted for the next cycle collection, because for them to become part of a garbage cycle, an unaccounted reference to a C++ object would need to be dropped, causing the necessary objects to be traversed by the placement of that C++ object in the purple buffer.  I *think* we could do this by either adding an additional color or two to the cycle collection algorithm (in a manner equivalent to the following alternative) or by doing the marking-black (black==uncollectable) in the cycle collector in phases: first mark black based on unaccounted references to C++ objects, then pause and note which wrappers are already marked, and then mark black based on unaccounted references to JS objects.  (Or is there even such a thing as an unaccounted reference to a JS object?  I suppose, with this algorithm, there would be, even if there isn't now.)  The wrappers that were so marked would be added to the set that do not need to be noted in the next collection.  Any wrappers that were traversed but not marked in the first phase would be removed from the set.  Any wrappers that were not traversed at all would be left in their current state (i.e., we wouldn't change whether or not they were in the set).  (I've only thought this through roughly; before moving further we'd definitely need to think it through more carefully to check that it ought to work.)

Whether this is worthwhile depends on a bunch of things.  My gut feeling, though I haven't measured it, is that, thanks to bug 378987, most of the objects that we traverse during cycle collection are JS objects.  However, even if we do this, we'll still need to build up the implicit reference counts for the entire JS runtime.  So the question is whether saving traversal (and thus the cycle collection algorithm) for most JS objects would be a significant enough savings to be worthwhile even if we are still building the implicit reference counts.

So, in summary, I think this is likely to be either low priority or not worth doing at all, but I'm not sure about that, and I wanted to write it down before I forgot about the idea in case I'm wrong.
One problem with that is that we currently rely on the cycle collector to mark the objects held by XPConnect wrappers. We don't create roots for the JS objects in XPConnect anymore. After the mark phase of JS GC we add all the XPConnect wrappers' JS objects to the cycle collector. We then build the CC graph and color them either white or black. We then mark the black JS objects, so they don't get collected. Then the JS GC does its sweep. If we don't add all wrappers' JS objects to the cycle collector some of those objects might get collected by the JS GC even though they're still live.
There's an interesting idea in here, but I don't think this is safe to do right now without being very careful.

Some possible problems that would have to be dealt with:
- If two wrappers reach the same JS object, then if we traverse one we must traverse the other, so you'd have to somehow track when that happens, or not skip in that case.
- You'd also have to make sure that the wrapper doesn't reach any non-gray JS objects, as those can be touched by JS.
- I think that you'd also have to remove a wrapper from the skip-set whenever you ungray it, because I'm imagining scenarios where an object goes from gray to black to gray and invalidates the skip-set in between CCs.

Traversing a ton of JS is probably less of a problem than it used to be, due to not traversing through black objects.

I'm going to mark this WONTFIX.  Feel free to reopen.
Last Resolved: 7 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.