FinalizationRegistry
FinalizationRegistry lets you register cleanup callbacks that run when your objects are garbage-collected. It is available in all modern browsers and Node.js since April 2021.
Creating a registry
You instantiate FinalizationRegistry by passing a callback function:
const registry = new FinalizationRegistry((heldValue) => {
console.log(`Cleaning up: ${heldValue}`);
});
The callback receives whatever heldValue you passed when registering the target object.
Registering targets
Call register on any created registry to track an object:
const registry = new FinalizationRegistry((heldValue) => {
console.log(`Finalizing: ${heldValue}`);
});
const resource = { name: "database connection" };
registry.register(resource, "database connection");
// If `resource` becomes unreachable, the callback fires with "database connection"
When the target object has no remaining references and the garbage collector reclaims it, the callback fires asynchronously. The callback never runs synchronously during registration.
Register parameters
register(target, heldValue) takes two required arguments:
- target — the object to track. This must be an object or a non-registered symbol.
- heldValue — passed to your callback when the target is collected. Can be any value: primitives, objects, functions. The registry keeps a strong reference to objects here so it can deliver them to your callback later.
register(target, heldValue, unregisterToken) adds a third optional argument used to unregister the target before collection:
registry.register(resource, "database connection", resource);
If you omit the unregisterToken, the target cannot be unregistered later.
Return value and errors
register returns undefined. It throws a TypeError if:
targetis not an object or a non-registered symboltarget === heldValueunregisterTokenis not an object or a non-registered symbol
Unregistering targets
If you need to cancel a registration before the target is collected, call unregister with the same token you passed as the third argument:
const registry = new FinalizationRegistry((heldValue) => {
console.log(`Finalizing: ${heldValue}`);
});
const buffer = new ArrayBuffer(1024);
registry.register(buffer, "buffer", buffer);
// Later, when you're done with the buffer manually:
registry.unregister(buffer);
// Returns true — the registration was found and removed
unregister returns true if at least one registration matched the token and was removed, false if no registration matched. Multiple targets registered with the same token are unregistered together.
It throws a TypeError if unregisterToken is not an object or a non-registered symbol.
You only need unregister if you want to cancel a cleanup callback you previously registered. Do not call it from inside the cleanup callback itself.
Common usage pattern
FinalizationRegistry is useful for cleaning up native resources that JavaScript objects wrap:
class DatabaseConnection {
static #registry = new FinalizationRegistry((id) => {
console.log(`Closing orphaned connection: ${id}`);
});
#id;
#connection;
constructor(id) {
this.#id = id;
this.#connection = this.#connect();
// Register cleanup using `this` as the unregister token
DatabaseConnection.#registry.register(this, id, this);
}
#connect() {
// actual connection logic
return { id: this.#id, close: () => {} };
}
release() {
this.#connection.close();
// Cancel the finalizer — we already cleaned up
DatabaseConnection.#registry.unregister(this);
}
}
The registry does not hold a strong reference to the target itself (otherwise the target could never be garbage-collected). It holds a strong reference to the heldValue, which is how it delivers data to your callback after the target is gone.
What FinalizationRegistry does not do
The callback does not fire immediately when the last reference drops. Garbage collection is non-deterministic — it runs when the engine decides memory pressure warrants it. In some browsers, callbacks may not fire at all if the page is closed before collection.
Do not use FinalizationRegistry for critical program logic. Treat cleanup callbacks as best-effort hints that resources are being freed, not as guaranteed finalizers.
See Also
- WeakRef — a weak reference that does not prevent garbage collection
- WeakMap — a map holding weak references to its keys
- WeakSet — a set holding weak references to its values
- Memory management guide — how JavaScript manages memory and garbage collection