Python 3.14 Release Internals
CPython 3.14 (the reference Python interpreter; the “C” is for the C language it is written in) was released 7 October 2025, with the current patch release 3.14.5 on 10 May 2026 (python.org 3.14.5 release page). Most release notes catalogue new syntax and standard-library functions; this note instead isolates the changes that touch the internals — the runtime, the compiler, the garbage collector, and the threading model. Four of them dominate: free-threaded Python became officially supported but still opt-in (PEP 779, building on PEP 703); an experimental copy-and-patch just-in-time compiler now ships in the Windows and macOS binaries but is off by default and cannot run with free-threading (PEP 744); deferred (lazy) evaluation of annotations became the default behaviour (PEP 649/749); and — the cautionary tale of the release — the incremental garbage collector that shipped in 3.14.0 was reverted to the 3.13 generational collector in 3.14.5 after production memory-pressure regressions. This is the “version archaeology” anchor for the Python Internals MOC: a release where even a merged internals change was undone.
Why a Release-Internals Note Exists
Python’s facts decay with each release, and 3.14 is an unusually dense node for internals-relevant change. A claim like “free-threading exists” or “Python has a JIT” is meaningless without the version, the status (experimental vs. supported vs. default), and the build flag that gates it. Conflating these is the single most common way Python internals lore goes stale: people say “Python 3.14 has a JIT” and assume it runs by default, or “the GIL is gone in 3.14” and assume the standard build dropped it. Both are wrong in instructive ways. This note pins each change to its Python Enhancement Proposal (PEP — the design-document process by which Python evolves), its release status, and the configure-time or run-time switch that controls it, so that downstream notes can cite a single dated source.
graph TD R314["CPython 3.14.0<br/>(7 Oct 2025)"] --> FT["Free-threading:<br/>officially supported,<br/>opt-in (PEP 779)"] R314 --> JIT["Experimental JIT:<br/>ships in Win/macOS binaries,<br/>off by default (PEP 744)"] R314 --> TC["Tail-call interpreter:<br/>opt-in build,<br/>Clang 19+"] R314 --> SUB["concurrent.interpreters:<br/>subinterpreters in stdlib (PEP 734)"] R314 --> ANN["Deferred annotations:<br/>now the DEFAULT (PEP 649/749)"] R314 --> TSTR["t-strings:<br/>new syntax (PEP 750)"] R314 --> IGC["Incremental GC<br/>(3.14.0–3.14.4)"] IGC -->|"reverted over memory<br/>pressure regressions"| GEN["3.14.5: back to<br/>3.13 generational GC<br/>(10 May 2026)"] style FT fill:#cfe8cf style JIT fill:#ffe8b3 style IGC fill:#ffd6d6 style GEN fill:#cfe8cf
Diagram: the internals-relevant surface of 3.14. The insight is that each branch carries its own status — “supported but opt-in,” “experimental and off by default,” “now the default,” “reverted” — and these statuses, not the feature names, are what determine whether a given claim about 3.14 is true. The GC branch (red→green) is the one that changed within the 3.14 patch series.
Free-Threading: Officially Supported, Still Opt-In (PEP 779)
The headline internals change is the maturation of free-threaded CPython — a build of the interpreter that runs without the Global Interpreter Lock (GIL), the mutex that normally allows only one thread to execute Python bytecode at a time. The mechanism was designed in PEP 703 (Sam Gross, “Making the Global Interpreter Lock Optional in CPython”) and first shipped as an experimental build in 3.13. PEP 703 laid out a three-phase rollout: Phase I, experimental; Phase II, officially supported but optional; Phase III, default.
3.14 reaches Phase II. PEP 779, “Criteria for supported status for free-threaded Python” (status Final), is the gate the Steering Council defined for that transition (PEP 779). It is not itself a feature PEP — it is the acceptance checklist. Its specification set concrete bars for Phase II: single-threaded performance within 15% of the GIL-enabled build, memory within 20%, stable APIs without breaking changes, and improved internal documentation. The whatsnew text states plainly that “free-threaded Python is officially supported” as of 3.14 (What’s New in 3.14).
Three nuances matter for accuracy. First, officially supported is not default: the free-threaded interpreter is a separate build you must opt into, produced by configuring with --disable-gil, which “Defines the Py_GIL_DISABLED macro and adds \"t\" to sys.abiflags” (configure docs, v3.14.5; added in 3.13). The resulting binary carries that t ABI flag in its name: the CPython documentation states the free-threaded executable is “usually called python3.13t or python3.13t.exe” (whatsnew 3.13 source, v3.14.5 tag) — the 3.14 equivalent being python3.14t. The default standard build still has the GIL.
Second, the single-threaded overhead is real but modest. The free-threading HOWTO reports that on the pyperformance benchmark suite, “the average overhead ranges from about 1% on macOS aarch64 to 8% on x86-64 Linux systems” (free-threading HOWTO); the whatsnew phrases the same penalty as “roughly 5–10%, depending on the platform and C compiler used.” The figures are platform-specific, so cite the range rather than a single number. Crucially, 3.14 enabled the specializing adaptive interpreter (the runtime bytecode-optimization machinery from PEP 659) in the free-threaded build, which is much of why the overhead dropped into single digits — in 3.13 the free-threaded build had to disable specialization for thread-safety.
Third, the C API discipline changed: on Windows, extension modules built for the free-threaded ABI “must now specify Py_GIL_DISABLED” explicitly. The whole mechanism — biased and deferred reference counting, per-object locking — is the subject of Free-Threaded CPython; this note only pins the release status.
The Experimental Copy-and-Patch JIT (PEP 744)
3.14 is the first release whose official binary installers for Windows and macOS ship with the experimental just-in-time (JIT) compiler available (What’s New in 3.14). A JIT compiles parts of a program to native machine code while the program runs, rather than interpreting bytecode one instruction at a time. CPython’s JIT is described in PEP 744, “JIT Compilation” (status Draft, Informational) (PEP 744).
The technique is copy-and-patch: instead of a heavyweight compiler backend like LLVM running inside the interpreter, the build process pre-compiles small machine-code templates (one per micro-op) ahead of time using Clang/LLVM, and at run time the JIT stitches the templates together and patches in concrete values (jump targets, constants). PEP 744 frames this as letting “a high-quality template JIT compiler to be generated from the same domain-specific language used to generate the rest of the interpreter.” The JIT does not compile raw bytecode; it compiles the tier-2 micro-op traces produced by the tier-2 optimizer downstream of the specializing interpreter — so the JIT is the final stage of one pipeline that runs Specialize → tier-2 trace → machine code. See The CPython JIT Compiler and Copy-and-Patch Code Generation for the mechanism.
Two status facts are non-negotiable for accuracy:
It is off by default — and likely to stay that way. PEP 744 states the JIT “is currently not part of the default build configuration, and it is likely to remain that way for the foreseeable future,” and that until it is non-experimental it “should not be used in production, and may be broken or removed at any time without warning.” It is gated by the configure flag --enable-experimental-jit, added in 3.13 (not 3.14 — the JIT machinery predates 3.14; 3.14’s news is that the shipped binaries now include it). The flag takes values: no (default — don’t build the JIT), yes (build and enable; disable at run time with PYTHON_JIT=0), yes-off (build but disable by default; enable at run time with PYTHON_JIT=1), and interpreter (the JIT-interpreter, only for debugging the JIT itself) (configure docs).
It cannot run with free-threading (as of 3.14). The JIT and the free-threaded build are mutually exclusive in practice: the JIT is not yet thread-safe. The CPython project tracks making it so in issue #133171, “Make the JIT thread-safe”, and it is confirmed in CPython’s own build configuration that the combination is rejected: at the v3.14.5 tag, configure.ac contains the guard if test "$disable_gil" = "yes" -a "$enable_experimental_jit" != "no"; then ... AC_MSG_ERROR([--enable-experimental-jit cannot be used with --disable-gil.]) (configure.ac, v3.14.5, line 2828) — so configuring both hard-errors at the configure step rather than silently warning. The project tracks lifting this in issue #133171, “Make the JIT thread-safe”; free-threaded JIT support is expected to begin in a later release.
Uncertain
Verify: that free-threaded JIT support lands specifically in 3.15 (as of 2026-06-01). Reason: this is a future / unreleased schedule claim — 3.15 is still in development and the timeline can slip. The current behaviour (3.14.5 configure hard-errors on
--enable-experimental-jit+--disable-gil) is now primary-verified againstconfigure.acat the v3.14.5 tag and is no longer uncertain. To resolve: re-check issue #133171 and the 3.15 What’s New when 3.15 ships. uncertain
The Tail-Call Interpreter (Opt-In Build, Clang 19+)
Distinct from the JIT — and routinely confused with it — is the new tail-call interpreter, an alternative way of building the bytecode-dispatch loop itself. The conventional CPython evaluation loop is one giant C function with a computed-goto dispatch table (see Instruction Dispatch and Computed Gotos). The tail-call interpreter instead implements each opcode as a separate small C function that ends by tail-calling the next opcode’s function; with the right calling convention the compiler turns these tail calls into direct jumps, giving the CPU branch predictor a cleaner signal. The whatsnew describes it as “an internal implementation detail of the CPython interpreter. It doesn’t change the visible behaviour of Python programs at all” (3.14 whatsnew source, v3.14.5 tag).
It is gated by the configure flag --with-tail-call-interp, added in 3.14, which “specifically requires a C compiler with proper tail call support, and the preserve_none calling convention. For example, Clang 19 and newer supports this feature” (configure docs); a future GCC release is expected to support it. The whatsnew reports a “3–5% faster on the standard pyperformance benchmark suite” figure, with profile-guided optimization (PGO, --enable-optimizations) “highly recommended.” This is covered in depth in The Tail-Call Interpreter. The key accuracy point: the tail-call interpreter is not the JIT, has a different build flag, and emits no machine code — it only changes how the C interpreter dispatches.
Subinterpreters in the Standard Library (PEP 734)
CPython has supported multiple subinterpreters — independent interpreter states inside one process — at the C API level for decades, but they were unusable from pure Python. PEP 734, “Multiple Interpreters in the Stdlib” (status Final, accepted 5 June 2025), exposes them through the new concurrent.interpreters module in 3.14 (PEP 734). The module provides Interpreter objects (created via create()) and Queue objects (via create_queue()) for passing data between interpreters. A companion concurrent.futures.InterpreterPoolExecutor provides a pool-based interface.
The reason this is now worth doing is PEP 684 (“A Per-Interpreter GIL,” shipped 3.12), which gave each subinterpreter its own GIL — so subinterpreters can run Python bytecode on different cores genuinely in parallel, which threads under a shared GIL cannot. The whatsnew is candid about the rough edges: interpreter startup is “not yet optimized,” each interpreter “uses more memory than necessary,” object-sharing is limited (only memoryview can be shared directly), and many third-party C extensions are not yet subinterpreter-compatible (all stdlib modules are). Full coverage is in Subinterpreters and Per-Interpreter GIL.
Deferred Annotations Become the Default (PEP 649/749)
Before 3.14, a type annotation like def f(x: SomeType) was evaluated eagerly at function-definition time, which forced forward references to be written as strings (x: "SomeType") and made annotations a startup cost. 3.14 makes deferred (lazy) evaluation the default. As the whatsnew states, “the annotations on functions, classes, and modules are no longer evaluated eagerly. Instead, annotations are stored in special-purpose annotate functions and evaluated only when necessary” — except when from __future__ import annotations is in effect, which keeps the older string-based behaviour (What’s New in 3.14).
The design is PEP 649 (“Deferred Evaluation Of Annotations Using Descriptors”), and its concrete 3.14 implementation — the parts PEP 649 under-specified — is PEP 749 (“Implementing PEP 649,” status Final, targeting 3.14). PEP 749 “supplements rather than supersedes PEP 649… they do not change the general framework of the earlier PEP” (PEP 749). Concretely, the compiler now emits a per-object __annotate__ function that computes the annotations on demand, and a new standard-library module annotationlib introspects them through get_annotations() with a Format enum: VALUE (evaluate normally), FORWARDREF (replace undefined names with ForwardRef markers), and STRING (return the source text). With deferred evaluation, forward references no longer need to be quoted. The mechanics belong to Deferred Annotations; this note records that 3.14 flipped the default.
Template Strings — t-strings (PEP 750)
3.14 added a new string-literal form, the template string or t-string, written t'...'. PEP 750 (“Template Strings,” status Final, targeting 3.14) is the spec (PEP 750). Unlike an f-string, which evaluates t'Hello {name}' immediately to a finished str, a t-string evaluates to a Template object (in the new string.templatelib module) that preserves the static parts and the interpolations separately — a Template holds a tuple of literal strings and a tuple of Interpolation objects, each carrying the interpolated value, the original expression source text, an optional conversion (r/s/a), and the format_spec.
The motivation is safe, deferred interpolation: because the interpolations are not yet stringified, a processing function can intercept and transform each value before assembly — escaping HTML to prevent cross-site scripting, parameterizing SQL to prevent injection, or building domain-specific languages. PEP 750 states f-strings “provide no way to intercept and transform interpolated values before they are combined into a final string,” which is exactly the gap t-strings close. This is more a language feature than an internals one, but it adds a new literal kind to the tokenizer and compiler, so it earns a mention here.
The Garbage-Collector Story — Incremental, Then Reverted
The defining internals event of the 3.14 patch series is the reversion of the incremental garbage collector. This is the highest-value version fact in the whole release, and the one most easily gotten wrong.
CPython reclaims most objects immediately by reference counting, and runs a separate cyclic garbage collector to catch reference cycles that refcounting alone cannot free. Historically that cyclic collector is generational (objects are grouped into generations; younger generations are scanned more often — see Generational Garbage Collection). 3.14.0 shipped an incremental redesign instead, intended to shrink the worst-case GC pause by spreading a full collection over many small steps rather than doing it all at once.
In production it regressed memory. The discuss.python.org thread “Reverting the incremental GC in Python 3.14 and 3.15” records the decision (announced 16 April 2026). Release manager Hugo van Kemenade: “Python 3.14 shipped with a new incremental garbage collector. However, we’ve had a number of reports of significant memory pressure in production environments.” GC author Neil Schemenauer acknowledged the trade-off directly: pauses improved (one benchmark showed 1.3 ms vs. 26 ms), but “process memory use can be dramatically higher (5x was the worst case I saw) and runtime is slower.” Tim Peters invoked the patch-release principle: “The cardinal rule for patch releases is ‘first do no harm’… it’s in a fundamental area of the implementation, which affects all programs.”
The revert shipped in 3.14.5 (10 May 2026). The official 3.14.5 release page states it directly: “The incremental garbage collector shipped in Python 3.14.0–3.14.4 has been reverted back to the generational garbage collector from 3.13, due to a number of reports of significant memory pressure in production environments” (python.org 3.14.5). The same correction was applied to the 3.15 alpha line before its beta. So the precise statement is: 3.14.0 through 3.14.4 ran the incremental collector; 3.14.5 and later run the 3.13-style generational collector. The deeper lesson — that a merged, shipped internals change can be undone in a patch release, and that such a change arguably warranted a formal PEP — is the subject of The Incremental GC and Its Reversion. Cross-link there for the full mechanism and post-mortem.
Other Internals-Adjacent Changes
A few more 3.14 changes touch the runtime without rising to a feature of their own here:
- Remote debugging (PEP 768). A new, low-overhead interface lets a debugger attach to a running process by injecting a script via
sys.remote_exec(pid, script). It is on by default but can be disabled with thePYTHON_DISABLE_REMOTE_DEBUGenvironment variable, the-X disable-remote-debugflag, or the--without-remote-debugbuild option. Covered in Remote Debugging in CPython. - PGP signatures discontinued (PEP 761). Official releases no longer ship PGP signatures, moving to Sigstore-based verification.
- Emscripten at tier 3 (PEP 776). WebAssembly-in-the-browser via Emscripten became an officially supported platform at the lowest support tier.
Common Misunderstandings
- “Python 3.14 removed the GIL.” No. The free-threaded (GIL-less) interpreter is a separate, opt-in build (
--disable-gil); the default 3.14 build still has the GIL. PEP 779 made the free-threaded build officially supported (Phase II), not default (Phase III). - “Python 3.14 has a JIT now, so my code is faster.” The JIT ships in the Windows/macOS binaries but is off by default, experimental, not for production, and gated by
--enable-experimental-jit/PYTHON_JIT. The default interpreter does not JIT. - “The JIT works with free-threading.” It does not, as of 3.14 — the JIT is not thread-safe and the two builds are mutually exclusive. Expected to change in 3.15.
- “The tail-call interpreter is the JIT.” Different feature, different flag (
--with-tail-call-interp), no machine-code generation — it only restructures the C dispatch loop. - “3.14 uses an incremental garbage collector.” Only 3.14.0–3.14.4 did. 3.14.5+ reverted to the 3.13 generational collector. Always pin the patch version when discussing 3.14’s GC.
See Also
- The Incremental GC and Its Reversion — the full mechanism and post-mortem of the reverted collector
- Free-Threaded CPython — how the GIL-less build actually works (PEP 703)
- The CPython JIT Compiler — the copy-and-patch JIT mechanism (PEP 744)
- The Tail-Call Interpreter — the alternative dispatch loop
- Subinterpreters —
concurrent.interpretersand per-interpreter GIL (PEP 734/684) - Deferred Annotations — PEP 649/749 deferred-evaluation mechanics
- Python Release Cycle and Versioning — how Python versions and patch releases work
- The Specializing Adaptive Interpreter — the runtime optimizer the JIT sits downstream of
- Python Internals MOC — parent map (§1, Python in One Picture)