core23 min studyLesson 125 in Engineering

Reference counting: local release rules and cyclic retention

Trace strong and weak ownership edges, identify cycles, and separate atomic handle management from safe mutation.

On this page

What you’ll understand

  • Update an ordinary strong-reference count across ownership changes.
  • Explain why a closed strong cycle remains retained after external owners disappear.
  • Use weak edges without assuming they guarantee that a target remains alive.

Reference counting distributes release decisions across ownership operations. Acquiring another strong owner increments a count; dropping one decrements it; reaching zero permits destruction under the implementation's rules. This makes many lifecycles easy to follow, but a count answers a local question. A group of objects can keep one another counted even after the application has lost every path that could use them.

Count owners, not every observed pointer

For a conventional reference-counted object, distinguish an owning strong handle from a temporary borrow or a non-owning observation. Only ownership operations specified by the API affect the count. Copying an Rc handle through its clone operation adds a strong owner without cloning the payload. Borrowing through that handle does not create another independently owning payload. The implementation also keeps metadata, and exact object layout and count optimizations vary. Treat a simple integer count as a teaching model rather than a universal description of every runtime.

An atomic reference count coordinates concurrent ownership updates; it does not make arbitrary fields inside the object safe for concurrent mutation. Rust's Arc and Rc differ in thread-related guarantees, while the referent's synchronization requirements remain separate. A decrement that destroys a value can run arbitrary cleanup code and release further objects. Design code so a destructor or callback cannot observe an inconsistent container state. In language runtimes with reentrant finalization behavior, releasing a reference can have effects well beyond subtracting an integer.

cafter=cbefore+a−rc_{\rm after}=c_{\rm before}+a-r
a and r count newly acquired and released strong owners in an ordinary reference-count model, and c is a nonnegative integer. The equation assumes balanced, valid operations on a still-valid control structure; it does not specify immortal-object or deferred-counting runtime implementations.↔ Scroll wide equations horizontally.

A cycle has incoming edges from inside itself

Draw two objects A and B, with A strongly owning B and B strongly owning A. Initially an external handle also owns A. Removing that external handle leaves one internal strong owner for each object. Neither count reaches zero, so plain reference counting cannot start their destruction. Their retention does not require another thread or a malfunctioning counter. It follows from the graph. A larger cycle can include callbacks, observer registrations, closures, and parent-child relationships that are less obvious than two explicit fields.

A weak edge describes a relationship without keeping the target payload alive. For example, a parent may strongly own children while each child weakly refers back to its parent. Access through a weak handle must attempt an upgrade and handle failure if the last strong owner has disappeared. Depending on the API, weak handles can retain a control block after payload destruction. They therefore avoid a particular ownership cycle without promising zero metadata retention. Alternatively, explicit lifecycle code can sever edges, and some runtimes combine reference counting with cycle detection.

cA=eA+iA,cB=eB+iB,eA=eB=0, iA=iB=1c_A=e_A+i_A,\qquad c_B=e_B+i_B,\qquad e_A=e_B=0,\ i_A=i_B=1
e counts external strong owners and i counts incoming strong owners from inside a two-object cycle. Both resulting counts are one after external owners vanish. This graph model excludes special cycle collectors and assumes ordinary strong edges.↔ Scroll wide equations horizontally.

Check graph shape before tuning increments

When investigating retained objects, inspect ownership paths and their intended lifecycles before optimizing atomic increments. A permanent registry holding a strong handle is different from an isolated cycle, and both differ from a legitimately long-running request. Capture the path from a root or identify the closed strongly connected component. If the design intentionally keeps a shared cache, define eviction and size limits. Memory safety permits objects to remain live; it does not promise that every reachable or internally counted object is still useful to the application.

The probability lab offers a limited completion model. Suppose three independent external owners each release by a deadline with probability one half, and there are no internal strong edges. Set n=3 and p=0.5: the bar for three completions has probability one eighth. That is the probability all three have released under these assumptions. Raising p increases it. The lab does not manipulate reference counts, discover cycles, or model correlated owners. Add a strong A–B cycle on paper and explain why the same completion calculation no longer proves either payload can be destroyed.

Break one ownership edge

A and B each hold 128 bytes of payload. A strongly owns B, B strongly owns A, and one external handle owns A. Ignore metadata and other allocations.

  1. Initially A has count two: one external owner and B's owner. B has count one from A. Releasing the external handle leaves counts one and one.

  2. The closed cycle retains both payloads, totaling 256 bytes. No zero-count transition occurs under plain reference counting.

  3. Redesign B's edge to A as weak while A strongly owns B. When the last external A owner disappears, A can be destroyed and release B. With no other strong owners, B can then be destroyed; any weak-control metadata follows the API's separate rules.

The result

Changing one edge's ownership meaning removes this particular cycle. The weak edge must tolerate the target no longer being available.

Explore the model

Interactive lab

Interactive lab · probability

When have all external owners finished?

Set n=3 and p=0.5 for independent owners' deadline-completion events. The probability bar at three completions is 0.125; increasing p raises it. The lab assumes no internal ownership cycle and does not implement reference counting or cycle detection.

When have all external owners finished?These are exact binomial probabilities for independent trials with one fixed success probability. Playback tours the distribution; it does not resample outcomes. Current readings: Mean · np: 5; Variance · np(1−p): 2.5; Total probability: 1. All parameters are adjustable with the labeled controls below.03581000.070.150.220.3Successes kP(X = k)k = 0 · P = 9.77e-4
Exact probabilityHighlighted outcomeMean np
Mean · np
5
Variance · np(1−p)
2.5
Total probability
1
t = 0 s

These are exact binomial probabilities for independent trials with one fixed success probability. Playback tours the distribution; it does not resample outcomes. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forAll external owners finishing only implies release when no other strong ownership edges remain.

Check your understanding

Why does a closed two-object strong cycle survive after its external owners vanish under plain reference counting?

Try it yourself

A parent strongly owns two children. Each child weakly refers to the parent. There are no other strong owners. What happens after the sole external parent owner is released, and what must a surviving weak observer handle?

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Trace strong edges for payload retention; treat weak upgrade as fallible.

Compare with the solution

The parent loses its final strong owner and can release its child handles during destruction. Each child then loses its only strong owner and can also be destroyed. A surviving weak observer must handle a failed upgrade; its existence may retain control metadata but does not keep the parent payload alive under this ownership contract.

Take it with you

  • Strong ownership operations, rather than every pointer observation, determine ordinary reference counts.
  • Closed strong cycles retain objects without any counter bug.
  • Weak relationships avoid ownership retention but require handling an absent target.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.