NewsProgramming LanguagesPython

Python 3.14.5 Rolls Back the Incremental GC

Python 3.14.5 rolls back the incremental garbage collector — memory graph showing spike resolved
Python 3.14.5 reverts the incremental GC introduced in 3.14.0 after production memory pressure reports

Python 3.14.5 shipped on May 10, 2026, and its headline change is one you rarely see in a patch release: a full rollback of a major runtime feature. The incremental garbage collector introduced in Python 3.14.0 is gone. The generational GC from Python 3.13 is back. The reason is straightforward — the new collector caused memory usage to climb by up to 5x in long-running production services, and the core team moved fast to reverse course.

What the Incremental GC Was Supposed to Do

CPython has always used reference counting for memory management, with a cyclic garbage collector on top to handle reference cycles that reference counting cannot break. Before Python 3.14, that collector was generational: three generations, with gen0 collected most frequently and gen2 least. A full gen2 collection on a large, long-running process could pause execution for hundreds of milliseconds.

Mark Shannon’s incremental GC set out to fix this. Instead of stopping the world for a full collection pass, it collected objects in smaller interleaved steps, spreading the cost across many allocations. For large heaps, maximum pause times dropped by an order of magnitude. The design was technically sound. The production behavior was not.

What Actually Happened in Production

The incremental GC trades pause time for throughput. On short-lived scripts or CPU-bound workloads, that trade is often invisible. On long-running services — web servers, data pipelines, background workers — the math breaks down. The collector processes garbage in small increments, but if a service creates cyclic objects faster than the increments clear them, the backlog grows indefinitely.

Under the 3.14 collector, a full collection did not run until iteration 20,000. By that point, roughly 18,000 trash cycles were sitting in memory, unreclaimed. The steady state reached over 90,000 pending trash cycles. Multiple production deployments reported memory usage 5x higher than their Python 3.13 baseline.

Django contributor Adam Johnson documented an out-of-memory error triggered by running manage.py migrate on a resource-limited server after upgrading to Python 3.14. Django’s ORM creates plenty of cyclic objects during migrations. The incremental GC could not keep up. The workaround — forcing gc.collect() after each migration step — is now unnecessary with 3.14.5.

API Changes to Check Before Upgrading

For most Python developers, upgrading to 3.14.5 is transparent. If you have never touched the gc module directly, the change is invisible. However, if your code explicitly interacted with the GC API between 3.14.0 and 3.14.4, three behaviors changed:

  • gc.collect(generation) — Under the incremental model, gc.collect(1) ran one increment of garbage collection. In 3.14.5, it reverts to the 3.13 meaning: collect generation 1. These are not the same operation.
  • gc.get_count() — The incremental GC used two generations, so this returned a 2-tuple. It now returns a 3-tuple (gen0, gen1, gen2) again. Code that does gen0, gen1 = gc.get_count() will raise a ValueError.
  • gc.set_threshold() — The third threshold argument is meaningful again. If you tuned thresholds with only two values during 3.14.0–3.14.4, verify the default third value is appropriate for your workload.
# Safe on all versions
gc.collect()

# Changed behavior — review this
gc.collect(1)  # Was: run one GC increment (3.14.0–3.14.4)
               # Now: collect generation 1 (3.14.5+, same as 3.13)

# Check tuple arity
gen0, gen1, gen2 = gc.get_count()  # correct on 3.14.5+

The Sigstore Change Is Also CI-Breaking

Python 3.14.5 also drops PGP signatures for release artifacts entirely. The project has moved to Sigstore, which uses short-lived certificates and transparency logs instead of GPG keys. If your CI/CD pipeline verifies the integrity of Python release artifacts via signature checking, that step needs updating before you can upgrade cleanly.

Why This Matters Beyond the Fix

Patch releases do not reverse major internals changes. That Python 3.14.5 did so — and quickly — signals how serious the production impact was. It also surfaces a process issue: the incremental GC never went through the formal PEP process. It shipped as an internal implementation change without the structured public review that a modification this significant warranted.

The core team has acknowledged this. A reintroduction in Python 3.16 is on the table, this time via a PEP, which would allow thorough evaluation of the memory versus pause-time trade-off and potentially ship the feature as opt-in rather than default.

What to Do Now

Upgrade to Python 3.14.5. If you are on 3.14.0–3.14.4, the GC behavior is costing you memory in any long-running service. The upgrade is safe for the vast majority of applications. The only code that needs attention is any module that explicitly calls gc.collect(1) to run an increment, or that unpacks gc.get_count() as a 2-tuple.

If you are still on Python 3.13 and evaluating 3.14, this release removes the main production risk that was holding teams back. The free-threading and JIT features that make 3.14 worth the upgrade are still there. The memory trap is gone.

ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *

    More in:News