Programming & Compilers

How moving garbage collection preserves identity across addresses

Trace shared references through object relocation, then compare external raw addresses, managed handles, independent copies and pinning intervals.

By Republeq Editorial · 10 min read ·
Moving garbage collection traces shared identity and repaired references through object relocation, unmanaged addresses, handles, independent snapshots and pointer pinning intervals.

An object's current address is not necessarily its program-level identity. A runtime can move the storage of a live object while preserving the graph of references through which the program recognizes and uses it.

moving garbage collection depends on a continuity contract for those references. Every usable reference must be updated, redirected, or prevented from observing movement. A raw address held outside the runtime's control adds another obligation.

The examples use symbolic addresses and a stipulated collector model. They examine identity and address continuity rather than basic reachability marking, cycle collection or a particular runtime's implementation.

Trace object relocation through a shared-reference graph

Let live object O occupy symbolic address 1000. Two runtime references, rA and rB, refer to O. Another live object Q has a field that also refers to O.

All three incoming references identify one object, not three equal objects. If the program modifies O through rA, a later permitted read through rB refers to that same logical object. Q's field preserves the same alias relationship. Now stipulate that the collector moves O's storage to address 2000. It preserves O's required contents and repairs the three tracked incoming references so they identify the relocated object.

The final graph has rA, rB and Q's field referring to O at 2000. O's address changed, while the shared identity relationship did not. Copying the contents without repairing the references would be insufficient.

The runtime code replacement guide supplies a retained-reference comparison for object relocation. Changing an entry point does not automatically redirect an already-running frame or retained callback. Our collector model makes its corresponding repair obligation explicit: every usable reference to O must be handled when O's storage changes. The code guide concerns versioned code and access discipline; it does not implement garbage collection or prove our relocated graph correct.

Make the alias test concrete. O has a field count equal to 3 before movement. After its storage moves to 2000 and the three incoming references are repaired, a permitted update through rA sets count to 4. Reads through rB and Q's field must then read 4 from that same O. An independently copied snapshot still containing 3 has preserved an earlier value, not the shared mutation relationship.

The runtime also needs to handle references within the moved object according to its representation. If O contains a tracked self-reference, that field must continue identifying O rather than the abandoned storage at 1000.

Address continuity depends on the collector's reference contract

A collector can repair a reference only if it knows how that reference is represented and where it must be handled under the model. A root, an object field and a managed handle can have different representation rules.

The example assumes the collector tracks rA, rB and Q's field. It does not assume that every machine word numerically equal to 1000 is a reference that can safely be rewritten.

Relocation must also be coordinated with permitted program observations. A program cannot be allowed to use an old representation in a way that bypasses the collector's required repair or redirection mechanism.

The contract might provide controlled points for movement or another defined reference-access mechanism. Its details are runtime-specific. Merely knowing that a collector copies objects does not establish the conditions under which a program may use their addresses.

Identity comparisons, hashes and other application uses need to follow the runtime's stated identity rules. Treating the current raw address as a permanent identity would add a requirement the moving model does not supply.

An external raw address can remain behind

Add external component E, which receives raw address 1000 and stores it in an area the collector does not inspect or update. The runtime still knows that O is live through its tracked references.

After O moves to 2000, E's stored address remains 1000. The object remains live, but that external pointer no longer follows the intended object under this model. The abandoned address might no longer be usable or might later describe unrelated storage. The example makes no universal claim about its immediate contents. The continuity problem is that E lacks a guarantee of reaching O.

Keeping O reachable through rA is therefore insufficient. Liveness of the object and stability of E's particular address are different conditions.

The interface needs a defined mechanism for E's use interval: indirect access that follows movement, an independent copy where that semantics is suitable, or address stability under a pinning contract. A raw pointer alone names none of those mechanisms.

A handle follows the object through runtime indirection

Let E instead receive handle H. In the model, H is an identifier interpreted through a runtime-managed entry that currently refers to O. E does not treat H as O's raw storage address.

When O moves, the runtime updates the applicable handle entry from 1000 to 2000. E's handle value can remain unchanged while the runtime's interpretation follows the relocated object.

The access contract must say how E uses H. If it asks the runtime to perform an operation through the handle, the runtime can apply its defined reference rules. If it resolves H to a raw address and caches that address, the original raw-pointer continuity obligation returns.

Even resolving a current address immediately before use needs a guarantee covering the subsequent use interval if movement is permitted between those steps. Indirection alone does not justify an unprotected raw-address lifetime.

The handle also needs a validity and retention contract. Some handle types can keep an object live, while another design may not grant that guarantee. The identifier's existence does not establish every possible lifetime property.

An independent copy supplies different semantics

For an interface that needs a data snapshot rather than live object identity, the runtime can supply a separately owned copy under an explicit copying contract. E then uses that copy's storage and lifetime rules.

Movement of O no longer changes the independent copy's address relationship. But the copy is not the same logical object as O. Later changes to one do not automatically appear in the other. An independent snapshot can fit an immutable-data interface. It is insufficient for an application that needs E's changes to act on the original object through the shared reference graph.

If the application synchronizes edits back, that synchronization is another operation with defined semantics. The copy's initial value equality cannot establish ongoing shared identity.

Specify which continuity the interface requires: a stable snapshot of contents, a stable way to reach the current object, or a stable raw address for a particular interval.

How pointer pinning constrains movement during use

Under a stipulated pinning contract, O cannot relocate while the applicable pin remains active. E may use the current raw address during the permitted interval, subject to the contract's access conditions.

The structured concurrency guide provides a complete-use-interval comparison for pointer pinning. Cancelling a child or timing out a wait does not establish actual settlement before cleanup. A pin similarly needs to remain active until E's actual final raw-address use, even if an enclosing operation has already stopped waiting. The peer explains task-resource lifetime; keeping O at one address is an additional condition in this collector model.

The pin must cover every use of the exported raw address. Releasing it while E keeps a cached pointer for later use ends the stability guarantee before that later operation.

For one declared timeline, acquire the pin at time 0, export address 1000 at time 1, let E use it through time 4, and release the pin at time 5. Those uses are inside the pin interval. If E instead schedules another raw use at time 6, the old interval no longer protects it. The collector may move O to 2000 between release and that later use. The fault in this history is the uncovered use interval; release does not itself prove that movement already occurred.

A pinning API might also retain the object, but that is an additional specified property. An analysis should record both liveness and movability conditions rather than using one pin label for all of them.

When the pin ends, the collector may move O under its normal rules. The contract does not promise that it moves immediately or that the old address remains valid until a convenient later time.

Duration and distribution restrict relocation choices

A short pin used during one bounded external operation restricts movement for that interval. An indefinitely retained raw pointer can require a correspondingly long restriction if the interface relies on pinning for continuity. Several pinned objects also create several immovable locations under the model. Other live objects might remain movable, but a collector cannot choose an arbitrary rearrangement that relocates the pinned ones.

For an illustrative set A, B, C, D and E, pin B and D. A, C and E can still be eligible to move under their rules. A proposed rearrangement that changes B's or D's address would violate the active pins.

The pin contract restricts the permitted rearrangements. It does not diagnose allocator fragmentation or claim that one pin stops every collector operation. Actual layout and collector mechanisms determine the broader consequences.

The distribution of pins therefore belongs in the movement model alongside their duration. A count of live objects alone would omit those address constraints.

Verify identity and external continuity separately

Preserve the initial graph, tracked reference representations and relocation operation. After movement, inspect rA, rB, Q's field and any self-reference to ensure they still identify one O under the runtime contract.

Then inspect E's interface separately. A handle needs its runtime access and validity rule; a copy needs independent ownership and snapshot semantics; a pin needs a permitted interval covering every raw-address use.

The raw-address counterexample should fail the continuity check even though O remains live. The copied-object case should not be reported as preserving shared mutation merely because its contents initially match.

A post-relocation mutation provides another identity check under the model. Change a permitted field of O through rA, then inspect that field through rB and Q's reference. They should observe the same logical object under the applicable access rules. An independent snapshot supplied to E has another contract and need not reflect that change. The outcomes should be judged against their stated continuity, rather than declaring every equal initial copy equivalent.

A successful collector status does not supply those interface-specific results. The relevant evidence includes which references were repaired and what external access mechanism actually remained valid.

Draw the managed reference graph beside the external-pointer row on the Lenovo IdeaPad 15.6-inch FHD touch laptop. The catalogue lists an i5-1334U processor, 512GB PCIe SSD storage and 16GB DDR4 memory. Record all three tracked references changing from 1000 to 2000 while E's untracked address stays 1000. The resulting disagreement remains visible even though the managed roots keep O live.

A pin-use table on the ASUS Vivobook 14 FHD laptop can keep acquisition, export, last use and release in separate columns. The product description lists a Core 5 120U processor and 16GB DDR4 memory. Add the proposed time-6 access after release at time 5; that row exposes an uncovered raw-address use. The actual runtime must supply the pin and access contracts before the same table can describe a real interface.

Moving garbage collection: relocation, handles and pinned pointers