Counting ownership

The count tracks defined ownership relationships, not every number that resembles an address. CPython distinguishes borrowed references from strong references; acquiring a strong reference brings a corresponding release obligation.

For ordinary deallocatable objects, releasing the last strong reference invokes the type’s deallocator. Destruction can run user code, so shared state must already be consistent when a release can destroy an object.

References: [1]

Why cycles survive

If A strongly owns B and B strongly owns A, removing all external owners can leave both counts positive. Waiting for zero alone cannot reclaim that isolated cycle.

Rust’s Rc uses non-atomic counting for single-threaded shared ownership; Arc serves a different, thread-safe role. Weak references can represent back-links without making every edge keep its target value alive.

Atomic counting protects shared ownership rather than arbitrary concurrent mutation of the payload. Rust Arc<T> is Send and Sync only when T meets the corresponding bounds; wrapping RefCell in Arc does not make its borrowing machinery thread-safe. Shared mutable data can instead combine Arc with Mutex or another suitable synchronization mechanism. Keeping a value alive and coordinating access to its contents are separate responsibilities.

References: [2] [4]

Weak references and supplementary collection

A weak reference must be checked before use because the value may already have been destroyed. Parent-to-child ownership with weak child-to-parent links is one possible tree design, provided it matches the intended lifetime.

Counting can coexist with another collector. CPython supplements reference counting with cyclic collection; a system does not gain cycle collection merely by adopting reference counts.

References: [2] [3]