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

  1. Managing External and Native Resources: Releasing memory buffers allocated in WebAssembly or native desktop add-ons when their JavaScript wrapper object is collected.
  2. Monitoring and Diagnostics: Detecting memory leaks or tracking object lifecycles in development and performance profiling environments.
  3. Third-Party Resource Teardown: Cleaning up references in complex architectures where explicit teardown methods (destroy(), close()) might be accidentally omitted.

Important Caveats and Limitations