skip to content

How do you decide whether a hot shared object should keep paying the per-share counting tax, or move to another reclamation discipline?

level: principalimportance: should knowfreq 36%

answer

  1. two costs, different variables
  2. churn against live set
  3. what does immediate release buy
  4. scarce non-memory resource changes the answer
  5. decide per object, write the number down

basics

~20 s

Weigh two costs with different shapes: counting is priced by ownership changes per unit of work, reachability by live-set size and puts nothing on the sharing path. Pay the counting tax where prompt, deterministic release is genuinely worth something.

solid answer

~50 s

Decide per object, not per program. Counting's cost is a function of **how often ownership changes**, so an object shared on every request is expensive regardless of its size, while a large object shared rarely is nearly free. Counting's benefit is **prompt, deterministic release at the last drop** — which is worth a great deal when the object holds a scarce non-memory resource or when footprint must fall the instant work ends, and worth almost nothing for plain memory in a service with headroom. A reachability-based scheme inverts both: nothing on the sharing path, and work proportional to what is live rather than to churn. The mature answer is usually a mix — count the few things whose release must be immediate, do not count what lives for the process, and let a reachability-based scheme handle the rest — and to set the throughput and footprint targets that make the choice measurable rather than aesthetic.

go deeper

for a junior

The one thing to carry: counting costs more the more often an object is shared, and it buys release at the exact moment the last user lets go.

for a middle

Be able to say what each discipline is priced by — ownership churn on one side, live footprint and examination frequency on the other — and why that makes the answer depend on the object rather than on the language.

for a senior

Show the measurement first: ownership changes per request against the target rate, then what the object actually holds. Recommend restructuring ownership before abandoning promptness for anything holding a scarce resource.

for a principal

Own the part that has no single right answer: the two costs are paid by different parties and become visible in different metrics, so state the throughput and footprint targets that make the decision falsifiable and record the number beside the object.

## Two costs with different shapes The decision is tractable because the two disciplines are expensive in unrelated variables. | | Reference counting | Reachability-based reclamation | |---|---|---| | Cost scales with | ownership changes per unit of work | size of the live set, and how often it is examined | | Where the cost lands | inline, on every thread that shares | in a separate step, at boundaries | | When an object dies | at the last drop, deterministically | when the next examination proves it unreachable | | Cost of an object nobody shares | near zero | still visited if it is live | | Cost of an object everybody shares | one contended word, per share | unchanged | Read the last two rows together and the rule falls out: **counting is priced by churn, reachability by live footprint.** A workload with a small live set and enormous ownership churn is the worst case for counting and the best case for the alternative. A workload with a huge live set and almost no sharing is the reverse. ## What promptness is actually worth here People defend counting on the grounds that it frees immediately, so the question to press is: **what does immediately buy, for this object?** - **A scarce non-memory resource** — an open operating-system handle, a connection, a lock, a lease, a file descriptor. The supply is small and hard-limited, so releasing at the last drop is not an optimisation, it is the correctness of the resource budget. This is the strongest case for paying the tax, and it survives almost any amount of contention. - **A footprint that must fall on a schedule** — a memory budget that has to drop when a job ends rather than at some later boundary, so the next job can be admitted. - **Destruction with an observable effect** — flushing, unregistering, notifying. Deterministic timing is part of the contract. - **Ordinary memory with headroom.** Here promptness buys nearly nothing: freeing at the last drop and freeing shortly afterwards are indistinguishable to everyone except a benchmark. The hot shared read-only table in a proxy is the last category and the first cost profile — maximum churn, minimum benefit — which is why it is the canonical wrong place to count. ## The decision, in the order to take it 1. **Measure the churn, not the object.** Ownership changes per request times request rate, against a single word. If that number is within the same order of magnitude as your target throughput, counting this object is a scaling ceiling and no tuning will move it. 2. **Ask what the object holds.** If the answer is a scarce non-memory resource, keep prompt release and buy back the throughput by restructuring ownership — own it at an outer frame, borrow below — rather than by abandoning the discipline. 3. **Ask how long it lives.** Process-lifetime data should not be counted at all. This is a decision, not an optimisation: it says the object is unreclaimable by construction. 4. **Consider deferral before switching.** Batched or deferred counting keeps most of the model and moves the updates off the fast path, at the cost of the promptness itself. 5. **Switch the discipline only for the objects that need it.** These are choices per object graph; a service can perfectly well count the resource-holding handful and leave everything else to a scheme that puts nothing on the sharing path. ## What makes this a judgment call rather than a calculation There is no universally right answer because the two costs are paid by different people. The counting tax is paid **inline, by every request**, so it shows up as steady-state throughput and as a scaling curve that goes flat early — highly visible, evenly distributed, and it gets worse as you buy bigger machines. The alternative's cost is paid **in a separate step**, so it shows up as periodic work and as reclamation being observed later than the last use — less visible day to day, and concentrated in time. A team that is judged on tail behaviour and a team that is judged on cost per request will read the same two numbers and choose differently, honestly. What is *not* a judgment call is the failure mode of choosing by habit. Adopting one discipline for the whole program because it is the house style guarantees that some object sits in the wrong column: usually a widely shared, immortal, read-only structure paying a per-request tax for a reclamation that will never happen, or a scarce resource whose release has quietly become non-deterministic. The decision belongs to the object, and it should be written down next to it, with the number that justified it.

  • Why is an object holding a scarce operating-system resource the strongest case for paying the tax?
    Because the supply is hard-limited and small, so the time between the last use and the release is the time the resource is unavailable to anyone else. Under load that gap becomes exhaustion, which fails requests rather than merely slowing them. Deterministic release at the last drop converts a capacity risk into a bounded one, which is usually worth far more than the throughput it costs.
  • Can both disciplines coexist in one program, or is it a single global choice?
    They coexist routinely, and the mix is usually the right answer. Resource-holding objects are counted so their release is deterministic, process-lifetime data is not counted at all, and the bulk of ordinary objects are left to a scheme that puts no work on the sharing path. The cost of mixing is conceptual: engineers must know which rule applies to which object.

saying these in an interview costs you the question

  • Picks one discipline for the whole program on principle
  • Argues counting is always cheaper because it has no separate step
  • Judges the cost by the object's size rather than by how often it is shared
  • Treats prompt release as valuable even for plain memory with headroom
  • Forgets that a process-lifetime object is being counted toward a zero it never reaches
  • Claims bigger machines will absorb the counting tax