FinalizationRegistry 对象回收监听

在 JavaScript 的内存管理中,ES12 (ECMAScript 2021) 引入了一个重要的新特性:FinalizationRegistry。这个特性为开发者提供了一种监听对象何时被垃圾回收的机制,填补了 JavaScript 在内存管理方面的一个重要空白。

什么是 FinalizationRegistry?

FinalizationRegistry 是一个构造函数,它允许开发者注册对象,并在这些对象被垃圾回收时执行回调函数。这为资源清理和内存管理提供了更精细的控制。

javascript 复制代码
const registry = new FinalizationRegistry(heldValue => {
  console.log(`对象已被回收,关联值为: ${heldValue}`);
});

基本用法

注册对象

要使用 FinalizationRegistry,首先需要创建一个实例,然后注册需要监听的对象:

javascript 复制代码
const registry = new FinalizationRegistry(heldValue => {
  // 回调逻辑
});

let obj = {};
registry.register(obj, "这是关联值");

取消注册

如果不再需要监听某个对象,可以取消注册:

javascript 复制代码
registry.unregister(obj);

使用场景

1. 资源清理

FinalizationRegistry 特别适合用于管理外部资源,如文件句柄、数据库连接等:

javascript 复制代码
const fileHandleRegistry = new FinalizationRegistry(handle => {
  // 确保文件句柄被关闭
  handle.close().catch(console.error);
});

async function openFile(path) {
  const handle = await fs.promises.open(path, 'r');
  fileHandleRegistry.register(handle, handle);
  return handle;
}

2. 缓存管理

在实现缓存系统时,可以监听缓存对象何时被回收:

javascript 复制代码
const cacheRegistry = new FinalizationRegistry(key => {
  console.log(`缓存键 ${key} 已被回收`);
  delete cacheMap[key];
});

const cacheMap = {};

function setCache(key, value) {
  cacheMap[key] = value;
  cacheRegistry.register(value, key);
}

注意事项

  1. 不可依赖:垃圾回收的时间是不确定的,不能依赖 FinalizationRegistry 来执行关键逻辑。

  2. 性能影响:过度使用可能会影响垃圾回收性能。

  3. 内存泄漏:如果忘记取消注册,可能会导致回调函数持有不必要的引用。

  4. 浏览器兼容性:虽然现代浏览器大多支持,但在旧环境中可能需要 polyfill。

与 WeakRef 的关系

FinalizationRegistry 通常与 WeakRef(弱引用)一起使用,WeakRef 允许你持有对象的引用而不阻止其被垃圾回收:

javascript 复制代码
const registry = new FinalizationRegistry(key => {
  console.log(`${key} 被回收了`);
});

let obj = { data: 'important' };
const weakRef = new WeakRef(obj);

registry.register(obj, 'myObject');

// 当 obj 被回收时,回调执行
obj = null;

结论

FinalizationRegistry 是 ES2021 中引入的一个强大工具,它为 JavaScript 开发者提供了更细粒度的内存管理能力。虽然它不应该用于关键的业务逻辑,但在资源清理、缓存管理等场景中,它是一个非常有价值的补充。正确使用这个特性可以帮助开发者编写更健壮、更高效的 JavaScript 应用程序。

在使用时,开发者应当充分理解其工作原理和限制,避免过度依赖或误用这一特性。