# 93. Immortal Objects

# 93. Immortal Objects

Immortal objects are objects whose lifetime is treated as effectively permanent by the CPython runtime. Their reference counts no longer behave like ordinary objects, and the interpreter avoids many normal reference counting operations on them.

Immortal objects were introduced as part of larger runtime optimization work, especially in support of free-threaded CPython and multicore scalability.

The main idea is simple:

```text id="jqm0uy"
some objects exist for the entire interpreter lifetime
incrementing and decrementing their reference counts is unnecessary
skipping refcount updates reduces synchronization overhead
```

This chapter examines:

```text id="d4aj08"
why immortal objects exist
how CPython traditionally managed object lifetime
why reference counting becomes expensive
how immortality works internally
which objects become immortal
how the object header changes semantically
how immortal objects interact with free-threading
what risks and tradeoffs appear
```

Immortal objects change one of the oldest assumptions in CPython: that every object participates uniformly in reference counting.

## 93.1 Traditional Reference Counting

Historically, every CPython object maintained a mutable reference count.

Conceptually:

```c id="0np5t0"
typedef struct {
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
} PyObject;
```

Every new strong reference increased the count:

```c id="vy5xqy"
Py_INCREF(obj);
```

Every released reference decreased the count:

```c id="jlwm7z"
Py_DECREF(obj);
```

When the count reached zero:

```text id="u1v1o3"
object destructor executes
memory is released
contained references are decremented
```

This model applied to nearly every object in the runtime:

```text id="1ykzva"
integers
strings
lists
dicts
functions
modules
types
singletons
```

Even globally shared objects participated fully in refcount operations.

## 93.2 Why Reference Counting Becomes Expensive

Reference counting is simple conceptually, but expensive at scale.

Each increment and decrement requires memory writes:

```c id="j9yqlg"
++obj->ob_refcnt;
--obj->ob_refcnt;
```

In traditional single-threaded execution, this overhead was acceptable.

Under multicore execution, the cost grows dramatically.

Consider:

```python id="nq2jww"
x = None
```

This may appear trivial, but internally:

```text id="fmslfm"
load reference to None
increment refcount
store pointer
later decrement refcount
```

Now imagine millions of threads repeatedly touching shared objects:

```text id="2j4tjq"
None
True
False
small integers
interned strings
builtin types
```

These objects become synchronization hotspots.

Under free-threaded execution:

```text id="09a9d0"
multiple CPU cores modify same refcount field
cache lines bounce between cores
atomic operations serialize updates
```

Reference counting traffic alone can dominate execution cost.

## 93.3 The Core Observation

The key observation behind immortal objects is:

```text id="q3x7v4"
many runtime objects effectively never die
```

Examples:

```python id="ajkh8h"
None
True
False
0
1
2
""
()
int
str
list
dict
object
type
```

These objects typically survive until interpreter shutdown.

Therefore:

```text id="ltm37r"
tracking exact reference counts provides little value
```

The runtime does not meaningfully benefit from repeatedly incrementing and decrementing them.

## 93.4 The Immortal Object Idea

Immortal objects use a special reference count state indicating:

```text id="36nm5h"
this object should never be deallocated
```

Conceptually:

```text id="5t33pk"
normal object
    refcount changes dynamically

immortal object
    refcount treated as permanent
```

Instead of:

```c id="70zpnv"
Py_INCREF(Py_None);
Py_DECREF(Py_None);
```

the runtime may skip updates entirely.

This reduces:

```text id="79wbdb"
atomic synchronization
cache contention
memory writes
cross-core coordination
```

especially in highly parallel execution.

## 93.5 Immortal Reference Counts

Immortality is represented through special reference count values.

Conceptually:

```text id="l8mwja"
very large sentinel refcount
or
special immortal bit pattern
```

The runtime recognizes:

```text id="5k0fui"
this object should never reach zero
```

Therefore:

```c id="mgd2tp"
Py_INCREF(obj);
```

can become:

```c id="sj2nb1"
if (!immortal(obj)) {
    increment_refcount(obj);
}
```

Similarly:

```c id="f73by4"
Py_DECREF(obj);
```

may skip decrement logic entirely.

The object effectively exits normal lifetime accounting.

## 93.6 Which Objects Become Immortal

Immortality is most useful for heavily shared runtime objects.

Typical candidates:

| Category | Examples |
|---|---|
| Singletons | `None`, `True`, `False` |
| Small integers | `-5` through cached positive integers |
| Interned strings | Frequently reused identifiers |
| Builtin types | `int`, `str`, `list`, `dict` |
| Static runtime objects | Global runtime metadata |
| Permanent constants | Empty tuple, empty bytes |

These objects are:

```text id="v6a8pj"
globally shared
frequently referenced
rarely or never destroyed
```

They are ideal immortality candidates.

## 93.7 Why Small Integers Matter

Small integers are touched constantly.

Example:

```python id="1b4npa"
for i in range(1000000):
    x = i + 1
```

Even simple loops repeatedly reference:

```text id="2yfxgq"
0
1
2
small counters
temporary arithmetic values
```

Without immortality:

```text id="p7s19n"
refcount increments occur constantly
```

Under free-threaded execution:

```text id="yvrfph"
atomic refcount traffic becomes enormous
```

Immortal small integers significantly reduce synchronization pressure.

## 93.8 The Empty Tuple Optimization

The empty tuple is another important case.

Example:

```python id="27n5od"
()
```

Many runtime operations reuse the same empty tuple object:

```text id="csk2mj"
function defaults
empty argument tuples
internal APIs
temporary state
```

Without immortality:

```text id="c7gm3z"
global refcount contention appears repeatedly
```

Making the empty tuple immortal eliminates unnecessary updates.

## 93.9 Immortality and Free-Threaded CPython

Immortal objects are especially important in free-threaded CPython.

Traditional refcount updates:

```c id="7mqw8w"
++obj->ob_refcnt;
```

become unsafe under parallel execution.

Free-threaded CPython therefore uses atomic operations:

```c id="f8zv4y"
atomic_fetch_add(...);
```

Atomic operations are expensive.

Immortal objects reduce this cost dramatically:

```text id="0w8k5h"
shared singleton objects avoid atomic traffic entirely
```

This improves scalability across multiple CPU cores.

Without immortality, free-threaded reference counting overhead would be substantially worse.

## 93.10 Object Headers Still Exist

Immortal objects still use normal object structures.

Conceptually:

```c id="tx92qs"
typedef struct {
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
} PyObject;
```

The difference is semantic:

```text id="4qzmg9"
refcount no longer represents actual ownership count
```

Instead:

```text id="xjlwm0"
refcount acts as immortal sentinel state
```

The object header format largely remains compatible.

This is important for ABI stability.

## 93.11 CPython Still Uses Reference Counting

Immortal objects do not replace reference counting entirely.

Most objects remain ordinary reference-counted objects:

```python id="v31v0s"
x = [1, 2, 3]
```

This list still behaves normally:

```text id="8ytm78"
increment references
decrement references
destroy when refcount reaches zero
```

Immortality is selective.

The runtime applies it only where beneficial.

## 93.12 Why Not Make Everything Immortal

Making all objects immortal would create severe problems.

Example:

```python id="7t2mh4"
while True:
    xs = [1, 2, 3]
```

If lists never died:

```text id="d2z0ij"
memory usage would grow forever
```

Immortality works only for objects whose lifetime is already effectively permanent.

The runtime therefore distinguishes:

| Object Type | Lifetime |
|---|---|
| Global singleton | Permanent |
| Temporary container | Dynamic |
| User object | Dynamic |
| Runtime metadata | Often permanent |
| Frame object | Dynamic |

Most objects must still be reclaimed normally.

## 93.13 Interaction With the Garbage Collector

Immortal objects interact differently with cyclic GC.

Because they are never destroyed:

```text id="07nk7h"
their lifetime no longer depends on refcount transitions
```

However:

```text id="82y5p6"
immortal objects can still reference ordinary objects
ordinary objects can reference immortal objects
```

The garbage collector must still reason about graph reachability correctly.

Immortality does not eliminate GC logic.

It mainly removes unnecessary destruction tracking for permanent objects.

## 93.14 Destructors and Finalization

Immortal objects never reach zero references.

Therefore:

```text id="4l4hzz"
their destructors never execute
```

This is acceptable because immortal objects are intentionally permanent.

Examples:

```python id="9m5wbj"
None
True
False
```

do not require cleanup logic.

Objects needing deterministic destruction are unsuitable for immortality.

## 93.15 Stable Object Addresses

Immortal objects also tend to have stable addresses.

Example:

```python id="z8h6sh"
id(None)
```

typically remains stable throughout interpreter execution.

This benefits:

```text id="ln4vsx"
caches
interning systems
runtime metadata
fast-path comparisons
pointer identity checks
```

The runtime can safely assume such objects persist indefinitely.

## 93.16 Fast Identity Checks

Python identity checks:

```python id="0muhyy"
x is None
```

depend on pointer identity.

Immortal objects strengthen assumptions around these patterns:

```text id="xjlwm1"
the object definitely survives
pointer remains valid
no destruction races occur
```

This becomes especially valuable under free-threaded execution.

## 93.17 Interned Strings and Immortality

Interned strings are another important case.

Python frequently reuses identifiers:

```python id="jlwmxg"
"name"
"value"
"append"
"dict"
```

Interning reduces duplication:

```text id="db8h6q"
multiple references share same string object
```

These strings often persist for the interpreter lifetime.

Immortality removes unnecessary refcount traffic for such shared identifiers.

This matters because:

```text id="kknnln"
attribute lookup
dictionary keys
module globals
compiler metadata
```

all heavily use interned strings.

## 93.18 Immortality and Type Objects

Builtin type objects are heavily shared.

Example:

```python id="3i7x8x"
int
str
dict
list
```

These are accessed constantly:

```text id="0mtrvk"
instance creation
attribute lookup
type checking
slot dispatch
```

Without immortality:

```text id="i6aqdx"
type object refcounts become synchronization hotspots
```

Immortal builtin types reduce runtime contention significantly.

## 93.19 ABI Compatibility

One important design goal is ABI stability.

Existing extensions assume:

```c id="kfrgt0"
PyObject contains ob_refcnt
```

Immortal objects preserve this structure.

The meaning changes slightly:

```text id="r3gvbo"
some refcounts no longer represent exact ownership totals
```

but binary compatibility largely survives.

This is critical because the CPython ecosystem contains enormous amounts of native extension code.

## 93.20 Borrowed References Become Safer

Immortal objects indirectly improve safety for borrowed references.

Example:

```c id="hmv7c9"
PyObject *x = Py_None;
```

Under immortality:

```text id="r3lq0u"
object definitely survives
destruction races disappear
```

This reduces certain lifetime hazards in concurrent execution.

However, immortality does not solve general borrowed-reference problems for mutable objects.

## 93.21 Cache Coherence Effects

Immortality improves hardware-level scalability.

Without immortality:

```text id="k4bjdz"
multiple cores update same refcount field
cache line invalidation occurs constantly
```

This creates coherence traffic:

```text id="ykjlwm"
core A modifies cache line
core B invalidates it
core A reloads it
```

Immortality removes many such writes entirely.

The effect can substantially improve multicore scaling.

## 93.22 Performance Implications

Immortal objects improve:

| Area | Benefit |
|---|---|
| Free-threaded scalability | Less atomic contention |
| Cache locality | Fewer invalidations |
| Singleton access | Faster |
| Small integer handling | Lower overhead |
| Type object access | Reduced synchronization |
| Interned string reuse | Lower traffic |

However, immortality also introduces costs:

| Cost | Reason |
|---|---|
| More runtime complexity | Special refcount states |
| More conditional logic | Skip checks |
| Less conceptual uniformity | Not all objects behave equally |
| Potential debugging confusion | Refcounts become nonliteral |

The runtime trades conceptual simplicity for scalability.

## 93.23 Debugging Immortal Objects

Immortal objects complicate debugging.

Historically:

```text id="dczrba"
refcount roughly represented ownership count
```

With immortality:

```text id="ccjlwm"
some objects never change counts meaningfully
```

Tools inspecting reference counts must understand:

```text id="0nh8di"
immortal sentinel values
special lifetime rules
nonstandard refcount semantics
```

Developers can no longer assume every object participates identically in memory management.

## 93.24 Immortality as a Runtime Optimization Layer

Immortality is fundamentally an optimization layer.

Python semantics remain unchanged:

```python id="e8e7a0"
None is None
```

still behaves identically.

The difference is internal:

```text id="mjlwm2"
less synchronization
fewer refcount updates
better multicore scalability
```

The optimization primarily exists to support modern runtime performance goals.

## 93.25 Future Directions

Immortal objects are part of a broader runtime evolution:

```text id="8yk3vc"
free-threaded execution
reduced global synchronization
better cache locality
scalable object lifetime management
```

Future runtime work may extend:

```text id="jlwm3x"
thread-local ownership
deferred refcount merging
regional allocation
object pinning
runtime specialization
```

Immortality is one step toward a more scalable interpreter architecture.

## 93.26 Chapter Summary

Immortal objects are objects whose lifetime is treated as permanent by the CPython runtime. Their reference counts no longer behave like ordinary ownership counters, and many refcount operations can be skipped entirely.

This optimization is especially important for free-threaded CPython, where atomic reference counting becomes expensive under multicore execution.

Immortal objects typically include:

```text id="jlwm4x"
singletons
small integers
builtin types
interned strings
empty containers
permanent runtime metadata
```

By eliminating unnecessary synchronization on heavily shared objects, immortal objects improve scalability, reduce cache contention, and support parallel interpreter execution.
