python / python/cpython

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

Abierto
#122,569 1 comentario 0 reacciones 1 asignado Ver en GitHub

@pablogsal ya está trabajando en esto.

Desde el 17/2/2025.

interpreter-core performance type-bug
Lenguaje dominante
Python
Estrellas
77.2k
Forks
35.9k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

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

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.