{"id":10931,"library":"gc-hook","title":"gc-hook: Simplified FinalizationRegistry","description":"gc-hook is a utility library that simplifies the use of JavaScript's `FinalizationRegistry` for managing object lifecycle and reacting to garbage collection. The current stable version is 0.4.1, with the last publish being a year ago, suggesting a mature but not rapidly evolving codebase. It differentiates itself by addressing common pitfalls associated with `FinalizationRegistry`, such as preventing accidental leaks of the registered reference, allowing flexible proxy overrides, and providing mechanisms for explicit deregistration via held references or tokens. Its primary goal is to abstract away the complex specifics of `FinalizationRegistry`, enabling developers to focus on application logic rather than the intricacies of memory management. It works in both CommonJS and ES module environments, offering broad compatibility.","status":"maintenance","version":"0.4.1","language":"javascript","source_language":"en","source_url":"https://github.com/WebReflection/gc-hook","tags":["javascript","FinalizationRegistry","simple","GC","Proxy"],"install":[{"cmd":"npm install gc-hook","lang":"bash","label":"npm"},{"cmd":"yarn add gc-hook","lang":"bash","label":"yarn"},{"cmd":"pnpm add gc-hook","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"Used for registering an object with a cleanup callback, returning a proxy by default. Both ESM and CommonJS imports are supported.","wrong":"const create = require('gc-hook').create;","symbol":"create","correct":"import { create } from 'gc-hook';"},{"note":"Used for explicitly unregistering a held reference from the GC hook.","wrong":"const drop = require('gc-hook').drop;","symbol":"drop","correct":"import { drop } from 'gc-hook';"}],"quickstart":{"code":"import { create, drop } from 'gc-hook';\n\n// Keep a count of all passed references created here\nlet references = 0;\n\n// Notify how many references are still around once collected\nconst onGarbageCollected = myUtility => {\n  console.log(--references, 'references still used');\n};\n\nconst createUtility = options => {\n  const myUtility = { ...options, do: 'something' };\n  console.log(++references, 'references provided');\n  // Return a proxy to avoid holding directly myUtility,\n  // while keeping the utility in memory until such proxy\n  // is not needed, used, or referenced anymore.\n  return create(myUtility, onGarbageCollected);\n};\n\n// As a module consumer:\nlet util = createUtility({ some: 'thing' });\nconsole.log('Utility created:', util);\n\n// Do something amazing with the util...\n\n// Simulate releasing the utility after some time\nsetTimeout(() => {\n  // Clear the utility or don't reference it anymore anywhere\n  util = null;\n  console.log('Utility reference cleared. Awaiting GC...');\n  // Once the GC kicks in, the module.js will log how many\n  // utilities are still around and never collected.\n  // Note: GC is non-deterministic, so the log might not appear immediately.\n}, 100);","lang":"javascript","description":"Demonstrates how to use `create` to register an object with a garbage collection callback, showing basic lifecycle management and explicit cleanup."},"warnings":[{"fix":"Design your application with robust fallback mechanisms for resource cleanup, such as explicit `dispose()` methods or other lifecycle hooks, rather than solely depending on GC for critical tasks.","message":"Garbage collection (GC) and thus `FinalizationRegistry` callbacks are inherently non-deterministic. Cleanup functions are not guaranteed to execute immediately, or even at all, if the process terminates before GC runs. Do not rely on them for essential program logic or timely resource release.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"If direct access to the original object or a custom wrapper is needed without proxy behavior, use the `return` option in `create(value, callback, { return: customWrapper })`.","message":"By default, `create` returns a `Proxy` around the registered object. This proxy is what prevents the original object from being prematurely garbage collected. Developers should be aware of proxy behavior, especially when relying on strict object identity checks or direct property enumeration, which might behave differently than with plain objects.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Carefully review cleanup callbacks to ensure they only use the provided `heldValue` argument and do not close over or retain strong references to the original `target` or its related state after it's meant to be unreachable. `gc-hook`'s design helps, but developer vigilance is still required.","message":"A common footgun with `FinalizationRegistry` is inadvertently creating a strong reference to the target object within the cleanup callback itself. While `gc-hook` is designed to mitigate this, developers must ensure their `onGarbageCollected` function does not capture or re-establish a strong reference to the object it is intended to finalize, which would prevent its garbage collection.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Understand and communicate that `FinalizationRegistry` is for 'best-effort' cleanup, not guaranteed resource release. For critical resources, implement explicit `close()` or `dispose()` methods. To observe GC activity more reliably in development, try running with `--expose-gc` flag in Node.js and manually calling `global.gc()`.","cause":"Garbage collection is non-deterministic and controlled by the JavaScript engine. It is not guaranteed to run at any specific time or even at all before a process exits. This is a fundamental characteristic of `FinalizationRegistry`.","error":"My cleanup callback function never runs, or runs much later than expected."},{"fix":"Thoroughly inspect all code paths for strong references. Use a memory profiler (e.g., Chrome DevTools heap snapshot) to identify what is still holding onto the object. Ensure the `onGarbageCollected` callback does not create new strong references to the object it's meant to clean up. If using `gc-hook/track`, remember it uses `console.debug`, which might have its own retention behavior in some environments.","cause":"There might still be a strong reference to the object preventing it from being garbage collected. This could be an accidental closure, a global reference, or an issue within the `onGarbageCollected` callback itself if it retains a strong reference to the `heldValue` or `target`.","error":"My object is still in memory even after I set all its references to `null`."},{"fix":"If you need to interact with the raw object or a specific wrapper, consider using the `return` option in `create`. For example, `create(ref, onGC, { return: ref })` would return the `ref` itself, though this negates the strong reference protection `gc-hook` provides and should only be done if you are sure about managing references externally.","cause":"By default, `gc-hook`'s `create` function returns a `Proxy` around the target object to prevent strong references. Direct interaction with this proxy might behave differently from the raw object, especially with operations that assume direct object identity or prototype chain traversal.","error":"I'm trying to access a property on the object returned by `create` but it behaves unexpectedly or causes errors related to Proxies."}],"ecosystem":"npm"}