JavaScript FinalizationRegistry Explained
The FinalizationRegistry object in JavaScript provides a
mechanism to request a callback function when a specific object is
reclaimed by the garbage collector. This article covers the purpose of
FinalizationRegistry, how to implement it with practical
code examples, its main use cases, and the critical caveats and best
practices you need to consider when managing memory cleanup.
What is FinalizationRegistry?
Introduced in ECMAScript 2021 (ES12),
FinalizationRegistry allows developers to register objects
and assign a cleanup callback that executes after those objects have
been garbage-collected.
JavaScript automatically manages memory via garbage collection,
meaning developers do not manually allocate and free memory. However,
when working with external resources—such as WebAssembly modules, native
C++ bindings, canvas contexts, or file system handles—JavaScript’s
engine cannot automatically free the associated non-JavaScript memory.
FinalizationRegistry bridges this gap by notifying your
code when the wrapper object is discarded.
How FinalizationRegistry Works
To use FinalizationRegistry, you create an instance
passing a cleanup callback, and then register target objects alongside
metadata (referred to as the “held value”) needed to perform the
cleanup.
Basic Syntax and Example
// 1. Create a registry with a cleanup callback
const registry = new FinalizationRegistry((heldValue) => {
console.log(`Object associated with '${heldValue}' has been garbage collected.`);
// Perform cleanup logic here (e.g., release external resources)
});
// 2. Define an object
let userSession = { id: "session_123", active: true };
// 3. Register the object with the registry
// Arguments: target object, held value, [optional unregister token]
registry.register(userSession, userSession.id);
// 4. Remove the reference to allow garbage collection
userSession = null;Unregistering Objects
You can unregister an object before it is garbage collected by passing an unregister token (often the object itself or another distinct object) during registration:
const unregisterToken = {};
// Register with a token
registry.register(targetObject, "metadata", unregisterToken);
// Unregister before collection occurs
registry.unregister(unregisterToken);Primary Use Cases
- Managing External and Native Resources: Releasing memory buffers allocated in WebAssembly or native desktop add-ons when their JavaScript wrapper object is collected.
- Monitoring and Diagnostics: Detecting memory leaks or tracking object lifecycles in development and performance profiling environments.
- Third-Party Resource Teardown: Cleaning up
references in complex architectures where explicit teardown methods
(
destroy(),close()) might be accidentally omitted.
Important Caveats and Limitations
- Non-Deterministic Timing: Garbage collection timing is non-deterministic and controlled entirely by the JavaScript engine. Cleanup callbacks may execute long after an object is dereferenced, or never at all if the application exits first.
- Not for Critical Logic:
FinalizationRegistryshould never be relied upon for critical business logic or essential state management. - Held Value References: The
heldValuepassed toregister()must not hold a strong reference to the target object. If it does, the target object will never be garbage collected, preventing the finalizer from ever running.