Why Instagram’s Python Servers Were Leaking Private Memory
Instagram’s frontend servers run Python (Django) and rely on a multi-process architecture backed by asyncio for concurrency within each process. To mitigate memory pressure at scale, the team uses a pre-fork server model where many objects are cached and shared read-only across processes via shared memory. However, over time, the servers’ private memory footprint grew while shared memory shrank.
Heap analysis pointed to the root cause: even objects that were practically immutable — living for the entire runtime lifetime — were still being mutated internally. Reference counting and garbage collection operations write to the object’s metadata on every read and each GC cycle, which triggers copy-on-write in the server processes. This hidden mutation of nominally static data was recreating per-process copies of what should have been shared memory.

The Mechanics of Immortalization
The problem stems from how CPython manages object lifetimes. The runtime relies on reference counting and cycle detection, both of which require writing to the core memory structure of every object. This is a fundamental reason Python needs the global interpreter lock (GIL).
To solve this, Instagram contributed PEP-683, which introduces immortal objects. An immortal object marks a special sentinel value in its reference count field. Once marked, the runtime knows not to mutate either that reference count or the object’s GC header. The object then stays alive for the entire runtime execution, but it can be shared freely without copy-on-write side effects.

Implementing this inside Instagram was straightforward, but the community-wide effort to merge it into CPython faced three major challenges:
- Backward compatibility: The change had to guarantee that applications would not crash if some objects suddenly reported different
refcountvalues. - Platform coverage: The change alters the core memory representation of Python objects and the way reference counts are incremented, so it had to work across Unix, Windows, and macOS; GCC, Clang, and MSVC; 32-bit and 64-bit architectures; and both little- and big-endian systems.
- Performance: The implementation adds explicit checks in the reference count increment and decrement paths, which are among the hottest code paths in CPython. With careful register allocation, the team kept the regression to about a 2 percent slowdown across all systems.
What This Changes for Instagram
Instagram’s immediate goal was reducing both memory and CPU overhead by eliminating copy-on-write events. With immortal objects in place, the team drastically reduced private memory usage and increased shared memory usage.

A Stepping Stone Toward Multi-Core Python
The significance goes beyond Instagram. Until now, CPython could not guarantee true immutability for any heap object because both the GC and reference count machinery had unrestricted write access to every object’s fields. Immortal objects introduce a real immutability guarantee for the first time.
Because immortal objects bypass both reference counting and garbage collection checks, they can be shared across threads without needing the GIL to ensure thread safety. This opens the door for true parallelism in two concrete proposals that leverage immortal objects differently:
- PEP-684: A per-interpreter GIL
- PEP-703: Making the global interpreter lock optional in CPython
The reference implementation is already merged into CPython, and the community is invited to evaluate how immortalization might apply to their applications — and to assess the proposals for a multi-core future before they land. Meta continues to develop and experiment internally while supporting the external contribution effort.



