Reference Counting
Reference counting is an alternative to tracing garbage collection in which each object maintains a count of how many references point to it. When the count reaches zero, the object is immediately deallocated. Unlike mark-and-sweep algorithms, reference counting reclaims memory incrementally and predictably — there are no global pause times, no stop-the-world events. The cost is paid in space (every object carries a counter) and in correctness (reference counting cannot detect cycles, where two or more objects mutually reference each other but are unreachable from program roots).
The cycle problem is not a minor edge case. It is a structural limitation. Any graph of objects with mutual references — parent-child relationships with back-pointers, doubly linked lists, observer patterns — will leak memory under pure reference counting unless the programmer manually breaks cycles or the runtime employs a secondary tracing collector to detect them. This hybrid approach — reference counting for the common case, cycle detection for the exceptions — is the strategy used by modern systems like the CPython interpreter and Apple's ARC for Objective-C and Swift.
Reference counting's true significance lies in its distributed nature. Where mark-and-sweep centralizes memory reclamation in a single collector thread, reference counting distributes the responsibility across every pointer assignment in the program. Every time a reference is created, copied, or destroyed, the count must be updated — atomically, in multithreaded environments. This fine-grained synchronization is expensive, and it is why high-performance systems generally prefer tracing collectors or compile-time ownership models like Rust's borrow checker over reference counting at scale.