GC - long_lived_pending is not decremented when a generation-2 object's ref count goes to 0
@pablogsal is already working on this.
Since Feb 17, 2025.
- Dominant language
- Python
- Stars
- 77.2k
- Forks
- 35.9k
- PR merge metrics
- PR metrics pending
Description
Bug report
Bug description:
Given the idea and documentation around the long_lived_pending / long_lived_total > 0.25 check, I would expect long_lived_pending to be the total number of objects that (1) have survived a generation-1 collection, and (2) are still live (i.e., ref count has not been decremented to 0).
From the behavior I'm seeing, I think only (1) is true. The example code below is contrived to promote many objects into generation-2 and then cause them to be deleted by their ref counts being decremented to 0. Hence there is no reason for Python to perform a generation-2 collection. Nevertheless, it does so repeatedly, collecting nothing each time. This seems sub-optimal, since these collections are expensive and are not doing anything.
import gc
import time
from typing import Any
class Temp:
pass
accumulating_list = []
def gc_callback(phase: str, info: dict[str, Any]) -> None:
generation = info['generation']
if generation == 2:
print(phase, info, len(gc.get_objects(2)))
if phase == "stop" and generation == 1:
# The GC that just ended will have promoted most of the Temps in
# accumulating_list into generation-2. Drop references to to them here,
# causing them to be immediately deleted.
accumulating_list.clear()
gc.callbacks.append(gc_callback)
gc.set_threshold(10, 2, 2)
for x in range(100000):
accumulating_list.append(Temp())
time.sleep(0.001)
Is it working as intended that long_lived_pending is not decremented when a corresponding object is deleted by its ref count dropping to 0? This would avoid generation-2 collections except where actually useful. It feels to me like this should happen, although perhaps it's infeasible or too expensive?
Note: Although this is a contrived example, we are seeing this in a real world largeish Python program. It's exacerbated by the default GC thresholds being set really low, which results in even short-lived objects (measured by time) being promoted quickly into generation-2.
CPython versions tested on:
3.11
Operating systems tested on:
macOS
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.