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.
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.