I’m not quite sure what you have in mind, and the gc code has changed a lot, but it looks the same in this respect to me as always
Which, I believe, used to be done by testing whether the gc header’s refcount copy “was negative”. For example, part of visit_decref
now:
if (gc_is_collecting(gc)) {
gc_decref(gc);
and before:
if (gc->gc.gc_refs > 0)
gc->gc.gc_refs--;
Some flags (one flag? more than one? unsure) were added to the gc header, but there was no room for them, so bit-fiddling was added and hidden behind layers of macros and inline functions.
Before, between collections every gc’able object’s recount copy was forced to one of two negative values (GC_UNTRACKED
or GC_REACHABLE
), and gc started by calling update_refs()
, which set the refcount copy to the actual (> 0!) refcount of the objects in the generation being collected.
In any case, that doesn’t have anything to do with the gc.get_referents()
I used to get the “over 6,000 objects reachable” result. That function is in the gc module, but doesn’t use any of its collection machinery - it just calls the argument’s tp_traverse
implementation directly. For example, here’s a goofy way to reverse a list
:
>>> gc.get_referents([1, 2, 12])
[12, 2, 1]
Yes, if the new refcount wasn’t zero, it would have to “do something”. Perhaps as “simple” as setting a persistent flag in the GC header. Except there’s no room for a flag.
As before, the real gain would come if the union of transitive closures of objects that survive decrefs is small compared to all objects. The paper described some success in some Java programs at running refcounting + this kind of cycle detection as opposed to using Java’s mark-&-sweep collector. Which is quite an achievement! Sun paid Guy Steele and a small office of PhDs to work on Java’s gc for a couple years - it’s good.
But Python objects can reach an awful lot of stuff, and Antoine spurred me to write that teensy test case which revealed just how extreme that’s become. I’ll note again that those thousand of objects were reachable from a module function (or class instance) in a module that only imported two functions, but not the modules (gc
and collections
) the functions came from - in a program that did nothing at all except import the module.
It’s probably not going to get better than that
.
I’m unclear on why someone might want to. If someone wants all gc’able objects in existence to be ignored forever after by gc, they can call gc.freeze()
now. Why would they want refcounting disabled too? That seems downright dangerous; e.g., there are a few places in CPython that trigger “optimizations” based on an object’s refcount, and they’re not safe if the refcount is lying.
If this is about a fork
micro-optimization, since I’m on Windows my answer to "what is fork
good for?` is “who cares?” :![:stuck_out_tongue_winking_eye: :stuck_out_tongue_winking_eye:](https://emoji.discourse-cdn.com/apple/stuck_out_tongue_winking_eye.png?v=12)