Skip to content

gh-159437: Skip unnecessary late weakref cleanup - #159590

Draft
mfalco-oai wants to merge 3 commits into
python:mainfrom
mfalco-oai:mfalco-oai/gh-159437-gc-weakref-skip
Draft

mfalco-oai wants to merge 3 commits into
python:mainfrom
mfalco-oai:mfalco-oai/gh-159437-gc-weakref-skip

Conversation

@mfalco-oai

@mfalco-oai mfalco-oai commented Oct 16, 2026 •

Copy link
Copy Markdown

This is stacked on the preceding counting change. That change counts collectable objects during clear_weakrefs() and removes the separate gc_list_size() walk. It still runs clear_weakrefs() for every collection.

With this optimization the GIL collector can skip that late cleanup pass. Callback preparation already visits the collectable objects, so it also records their count and whether weakrefs may need cleanup. If that scan finds no weakrefs that may need clearing and no Python code could have changed the garbage set, the collector reuses the count and skips clear_weakrefs().

When cleanup is needed, the collector uses the count returned by clear_weakrefs(), as in the preceding change. A resurrection check always takes this path because Python code may have changed the garbage set or created new weakrefs.

Count the stable unreachable set while preparing callbacks and retain a conservative cleanup flag. Reuse the count only when late weakref clearing has no work, and force fresh cleanup and counting after resurrection analysis.

Add a regression for a callback-free weakref observed when clearing a young cycle releases an older finalizer.
@python-cla-bot

python-cla-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

@bedevere-app bedevere-app Bot added the type-feature A feature request or enhancement label Oct 16, 2026

@pablogsal pablogsal left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep the counting change and hold off on this part until the application benefit is clearer. That already removes the separate counting pass. This adds a count before callbacks, then rules for deciding whether to reuse it or count everything again.

I tried parsing SQL with SQLglot, parsing Python with Parso and running Pylint, comparing this change with that version. There was quite a bit of noise and no clear application win. That leaves the extra cached count and cleanup state to maintain without enough evidence yet that they pay off.

Comment thread Python/gc.c
if (! _PyType_SUPPORTS_WEAKREFS(Py_TYPE(op))) {
// Garbage weakrefs need clearing even if their referents are outside
// this collection (bpo-38006).
needs_weakref_cleanup |= PyWeakref_Check(op);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, |= keeps calling PyWeakref_Check() after needs_weakref_cleanup is already true. We could stop checking at that point. Would need measuring whether the extra branch helps though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type-feature A feature request or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants