【问题标题】:What are WeakRef and Finalizers in ES2021 (ES12)什么是 ES2021 (ES12) 中的 WeakRef 和终结器
【发布时间】:2021-06-17 18:13:56
【问题描述】:

我想通过真正的简单示例在哪里使用它们来了解ES2021中的WeakRefFinalizers是什么。

我知道,WeakRef 是一个类。这将允许开发人员创建对对象的弱引用,并且 Finalizer 或 FinalizationRegistry 允许您注册将在对象被垃圾回收时调用的回调函数

const myWeakRef = new WeakRef({
  name: 'Cache',
  size: 'unlimited'
})

// Log the value of "myWeakRef":
console.log(myWeakRef.deref())

【问题讨论】:

标签: javascript ecmascript-2021


【解决方案1】:

一如既往,MDN's docs help

WeakRef 对象包含对对象的弱引用,该对象称为其目标或引用对象。对对象的弱引用是不会阻止对象被垃圾收集器回收的引用。相反,普通(或强)引用将对象保存在内存中。当一个对象不再有任何强引用时,JavaScript 引擎的垃圾收集器可能会销毁该对象并回收其内存。如果发生这种情况,您将无法再从弱引用中获取对象。

在 JS 的几乎所有其他部分中,如果某个对象 (A) 持有对另一个对象 (B) 的引用,则 B 不会被垃圾回收,直到 A 也可以被完全垃圾回收。例如:

// top level
const theA = {};
(() => {
  // private scope
  const theB = { foo: 'foo' };
  theA.obj = obj;
})();

在这种情况下,theB永远被垃圾回收(除非 theA.obj 被重新分配),因为顶层的 theA 包含一个包含对 theB 的引用的属性;这是一个强引用,可防止垃圾回收。

另一方面,WeakRef 提供了一个可以访问对象的包装器,同时不会阻止对该对象进行垃圾回收。在 WeakRef 上调用 deref() 将返回对象如果它还没有被垃圾回收。如果它已被 GC,.deref() 将返回 undefined


FinalizationRegistry 处理类似的问题:

FinalizationRegistry 对象允许您在对象被垃圾回收时请求回调。

您首先使用要运行的回调定义注册表,然后使用要观察的对象在注册表上调用.register。这会让你知道确切的时间什么时候垃圾收集。例如,一旦obj 被回收,以下将记录Just got GCd!

console.log('script starting...');

const r = new FinalizationRegistry(() => {
  console.log('Just got GCd!');
});
(() => {
  // private closure
  const obj = {};
  r.register(obj);
})();

您还可以在调用 .register 时传递一个值,该值会在对象被收集时传递给回调。

new FinalizationRegistry((val) => {
  console.log(val);
});
r.register(obj, 'the object named "obj"')

将记录 the object named "obj" 它会被 GC 处理。

综上所述,很少需要这些工具。正如 MDN 所说:

正确使用 FinalizationRegistry 需要仔细考虑,如果可能,最好避免使用。避免依赖规范未保证的任何特定行为也很重要。何时、如何以及是否发生垃圾收集取决于任何给定 JavaScript 引擎的实现。您在一个引擎中观察到的任何行为可能在另一个引擎中、在同一引擎的另一个版本中、甚至在同一引擎的相同版本中略有不同的情况下有所不同。垃圾收集是 JavaScript 引擎实现者不断完善和改进其解决方案的难题。

尽可能让引擎本身自动处理垃圾收集,除非您有充分的理由自己关心它。

【讨论】:

    【解决方案2】:

    弱引用的主要用途是实现对大对象的缓存或映射。在很多情况下,我们不希望长时间保留大量内存来节省这种很少使用的缓存或映射。我们可以让内存很快被垃圾回收,如果我们再次需要它,我们可以生成一个新的缓存。如果变量不再可访问,JavaScript 垃圾收集器会自动将其删除。

    const callback = () => {
      const aBigObj = {
        name: "Hello world"
      };
      console.log(aBigObj);
    }
    
    (async function(){
      await new Promise((resolve) => {
        setTimeout(() => {
          callback();
          resolve();
        }, 2000);
      });
    })();
    

    当执行上述代码时,它会在 2 秒后打印“Hello world”。根据我们使用 callback() 函数的方式,aBigObj 可能会永久存储在内存中。

    让我们将 aBigObj 设为弱引用。

    const callback = () => {
      const aBigObj = new WeakRef({    name: "Hello world"  });  console.log(aBigObj.deref().name);}
    
    (async function(){
      await new Promise((resolve) => {
        setTimeout(() => {
          callback(); // Guaranteed to print "Hello world"
          resolve();
        }, 2000);
      });
    
      await new Promise((resolve) => {
        setTimeout(() => {
          callback(); // No Gaurantee that "Hello world" is printed
          resolve();
        }, 5000);
      });
    })();
    

    第一个 setTimeout() 肯定会打印名称的值。这在创建弱引用后的事件循环的第一轮中得到保证。

    但不能保证第二个 setTimeout() 打印“Backbencher”。它可能已被垃圾收集器清扫。由于垃圾收集在不同浏览器中的工作方式不同,我们无法保证输出。这也是我们在管理缓存等情况下使用 Wea​​kRef 的原因。

    More Information...

    【讨论】:

      猜你喜欢
      • 2010-11-19
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 2011-08-18
      • 1970-01-01
      • 1970-01-01
      • 2021-01-16
      • 1970-01-01
      相关资源
      最近更新 更多