Skip to content

Class objects become immortal and cannot be GC after creating a new thread in free-threaded Python #124239

@XuehaiPan

Description

@XuehaiPan

Bug report

Bug description:

A simple reproduce script to print the refcount when there is main thread only and after creating a new thread.

# test.py

import gc
import sys
import threading
import weakref


Py_GIL_DISABLED = 't' in getattr(sys, 'abiflags', '')


class Foo:
    pass


print(f'refcount before: {sys.getrefcount(Foo)}')


# Start a new thread
thread = threading.Thread(target=str)  # no-op
thread.start()
thread.join()


print(f'refcount after:  {sys.getrefcount(Foo)}')


wr = weakref.ref(Foo)

# Ensure class Foo is collected
del Foo
for _ in range(10):
    gc.collect()


if Py_GIL_DISABLED:
    assert wr() is not None
    print('Class Foo is not collected!')
    print('sys.getrefcount(wr()):', sys.getrefcount(wr()))
else:
    assert wr() is None
    print('Class Foo is collected!')

On Python 3.13:

$ python3 test.py
refcount before: 5
refcount after:  5
Class Foo is collected!

On Python 3.13t:

$ python3 test.py
refcount before: 6
refcount after:  4294967295
Class Foo is not collected!
sys.getrefcount(wr()): 4294967295

CPython versions tested on:

3.13

Operating systems tested on:

macOS

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions