python / python/cpython

GC - long_lived_pending is not decremented when a generation-2 object's ref count goes to 0

Ouverte
#122,569 1 commentaire 0 réactions 1 personne assignée Voir sur GitHub

@pablogsal y travaille déjà.

Depuis le 17/2/2025.

interpreter-core performance type-bug
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

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

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.