【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

如何实现 JS 真正意义上的弱引用?

  • 2019-07-25
  • 本文字数:2558 字

    阅读完需:约 8 分钟

如何实现JS真正意义上的弱引用?

本文将详细解释 JavaScript 中对象的引用是强引用,WeakMap 和 WeakSet 可以提供部分的弱引用功能,若想在 JavaScript 中实现真正的弱引用,可以通过配合使用 WeakRef 和终结器(Finalizer)来实现。


一般来说,在 JavaScript 中,对象的引用是强保留的,这意味着只要持有对象的引用,它就不会被垃圾回收。


const ref = { x: 42, y: 51 };// 只要我们访问ref对象(或者任何其他引用指向该对象),这个对象就不会被垃圾回收
复制代码


目前,在 Javascript 中,WeakMap 和 WeakSet 是弱引用对象的唯一方法:将对象作为键添加到 WeakMap 或 WeakSet 中,是不会阻止它被垃圾回收的。


const wm = new WeakMap();{  const ref = {};  const metaData = 'foo';  wm.set(ref, metaData);  wm.get(ref);  // 返回metaData}// 在这个块范围内,我们已经没有对ref对象的引用。// 因此,虽然它是wm中的键,我们仍然可以访问,但是它能够被垃圾回收。
const ws = new WeakSet();ws.add(ref);ws.has(ref);// 返回true
复制代码


你可以认为 WeakMap.prototype.set(ref, metaData)是向对象 ref 中添加值为 metaData 的属性:只要你持有对象的引用,就可以获取元数据。一旦不再持有对象的引用,即使你仍持有添加了该对象的 WeakMap 的引用对象,也会被垃圾回收。类似地,可以将 WeakSet 视为 WeakMap 中所有值都是布尔值的一个特例。


JavaScript 的 WeakMap 并不是真正意义上的弱引用:实际上,只要键仍然存活,它就强引用其内容。WeakMap 仅在键被垃圾回收之后,才弱引用它的内容。这种关系更准确地称为ephemeron


WeakRef 是一个更高级的 API,它提供了真正的弱引用,并在对象的生命周期中插入了一个窗口。让我们一起来看个例子。


假设有一个 getImage 函数,它接受一个 name 入参,并执行一些昂贵的操作来生成另一个对象,比如生成二进制图像数据:


function getImage(name) {  const image = performExpensiveOperation(name);  return image;}
复制代码


为了提高性能,我们将图像保存在缓存中。现在,我们不必再为相同的入参执行昂贵的操作了!


const cache = new Map();
function getImageCached(name) { if (cache.has(name)) return cache.get(name); const image = performExpensiveOperation(name); cache.set(name, image); return image;}
复制代码


但是,这里存在一个问题。Map 会强保留它的键和值,因此,图像名称和数据永远不会被垃圾回收。这会逐步增加内存占用,最终导致内存泄漏


WeakRef 通过创建图像对象的弱引用并将弱引用保存在缓存中(而不是保存图像对象本身)来解决内存泄漏问题。这样,垃圾回收器就可以清除没有强引用的图像对象了。


const cache = new Map();
function getImageCached(name) { const ref = cache.get(name); if (ref !== undefined) { const deref = ref.deref(); if (deref !== undefined) return deref; } const image = performExpensiveOperation(name); const wr = new WeakRef(image); cache.set(name, wr); return image;}
复制代码


但这里仍然存在一个问题:Map 仍然永远保留 name 的字符串,因为这些字符串是缓存中的键。理想情况下,这些字符串也要被删除。 WeakRef 提案中也提供了一个解决方案!通过新的 FinalizationGroup API,我们可以注册一个回调,以便在垃圾回收器回收已注册的对象时运行。这种回调称为终结器(Finalizers)


注意:在垃圾回收器回收图像对象之后,终结回调不会立即运行。它可能在将来某个时候运行,甚至根本不运行——规范并不保证它一定运行!编写代码时,请注意这一点。


在此,我们注册一个回调,以便在图像对象被垃圾回收时从缓存中删除键:


const cache = new Map();
const finalizationGroup = new FinalizationGroup((iterator) => { for (const name of iterator) { const ref = cache.get(name); if (ref !== undefined && ref.deref() === undefined) { cache.delete(name); } }});
复制代码


注意:ref !== undefined && ref.deref() === undefined 是必需的,因为在旧 WeakRef 进入终结回调队列和实际运行终结回调之间,可能已经添加了同一个 name 的新 WeakRef


我们的最终实现如下:


const cache = new Map();
const finalizationGroup = new FinalizationGroup((iterator) => { for (const name of iterator) { const ref = cache.get(name); if (ref !== undefined && ref.deref() === undefined) { cache.delete(name); } }});
function getImageCached(name) { const ref = cache.get(name); // 1 if (ref !== undefined) { // 2 const deref = ref.deref(); if (deref !== undefined) return deref; } const image = performExpensiveOperation(name); // 3 const wr = new WeakRef(image); // 4 cache.set(name, wr); // 5 finalizationGroup.register(image, name); // 6 return image; // 7}
复制代码


给定一个图像名称(入参),我们在缓存中查找其对应的弱引用(1)。如果弱引用仍然指向某个对象(2),那么我们就返回缓存的图像数据。如果该图像名称对应的弱引入没有在缓存中,或者缓存的图像数据被垃圾回收了,那么我们计算图像数据(3),创建一个新的弱引用(4),将图像名称和弱引用保存到缓存中(5),注册一个终结器,一旦图像数据被垃圾回收,该终结器就删除缓存中图像名称(6),最后返回图像(7)。

过犹不及

听到这些新功能之后,可能会忍不住想要尝试 WeakRef 的所有这些功能(All The Things™)。不过,这可能不是个好主意。对于 WeakRef 和终结器来说,有很多明显不好的使用案例。


一般来说,不要编写依靠垃圾回收器清理 WeakRef 或者在任何可预测的时间调用终结器的代码——这是不可能的


例如,不要在终结器的代码块中放置重要的逻辑。因为,无法预测什么时候,甚至不确定是否会调用给定的终结器。最好将 WeakRef 和终结器视为渐进式增强:如果自定义终结器代码运行了,那最好;但是,在它没有运行的情况下,程序应该仍然可以正常运行。


WeakRef 和终结器可以帮助我们节省内存,并且在被当一种作渐进式增强的手段少量使用时,效果最佳。由于它们是高级用户特性,我们期望尽量只在框架或库中使用它们。

WeakRef 支持


关于此功能的支持列表:https://v8.dev/features/support


英文原文:https://v8.dev/features/weak-references


2019-07-25 18:415358
用户头像

发布了 257 篇内容, 共 148.2 次阅读, 收获喜欢 576 次。

关注

评论 1 条评论

发布
用户头像
一分钟大发快三技巧稳赢方式方法!金牌导师带你快速回血
老师359213571

如果你是刚刚玩,我来教教你,如果你已经玩很久了,却不稳,我来拉拉你,如果你已经遍体鳞伤,我来帮帮你。

我不能保证你一夜致富但希望能细水长流,汇聚江海,先要平稳的心态,不要一盘的失误影响你心情。

自己有规划性和目标性。做到这两点,过来找我我来帮你。

世界没有不努力就能盈利的。如果这些你觉得没时间精力去观察。那我只能劝你去跟计划了。最重要的还是你要学会耐心观察走势。

每种方法。只要你耐心观察。那种方法都可以盈利。以下是我最简单的跟号走势。

相信能进来看这贴的人都应该清楚快3是什么,又或者已经有人被它搞得伤痕累累。是不是你赢了一天,或一个星期的,

到最后都会在一个小时内就把你以前所赢的通通吐出来? 是不是你开始玩这个的时候你觉得钱来的实在太容易了?

你会发现你每天这样赢下去一个月后你就可以买辆宝马了? 但我告诉你,任何以堵钱的心态来玩的,你到最后都会输得一败涂地,

所以我们不要把它当成一种堵搏,把它当成一种投资的心态。又或者反它当成股票来操作。

以下我就说说几点;

1、不单要学会赢还要懂得怎样去止损,定下个止损目标是为了帮助自己在失手的时候有个损失限度,不受到翻本情绪的影响导致自己更加的错误下去。

这里就跟炒股一样,有止损就一定要有止赢,不要以为你赢那天你会一直赢下去,我告诉你,玩彩十个有九个都是一开始赢后面爆了帐户的。

彩是每天都会有,不要急着打回来,今天输了,明天还有机会。今天挂了。改日再战!

2、稳赚方法技巧是有的,但我在这里说的稳赚是最少以一个星期为单位,短期暴利模式网上一搜一大堆,但都拼不过变态期,

一把回到解放前很多人都经历过,快三方式很多接近稳赚,我告诉自己是稳赚,但我不敢这么跟大家说,因为没有什么事情是绝对的,何况堵搏。

为什么我说很多方法都可行呢?因为我不是叫你从早上九点到晚上12点都玩,那什么方法都是会回到解放前的。抓住重要,几把就可以收工了,

用得着这么麻烦吗?而且庄家最怕你什么?不怕你赢,就怕你赢了不玩。

3、止损与止赢的比例。我个人建设止损与止赢的比例定在1:1.。就是说比如你帐号是1W的,你今天的目标是赢1千,那你一定要做到赢1千就收,

同样的到你输1千的时候你也要收。不要跟我说拿1W只赢1千很小,不合理。

我相信这么多人玩彩的没有多少人能做到平均每天10%利润,我估计99%的人都做不到,其实不是他们的技术做不到,是他们的心态做不到。
展开
2019-07-25 21:27
回复
没有更多了
发现更多内容

(28DW-S8-Day16) 在线教育体验课

mtfelix

28天写作

正则表达式.03 - 分组

insight

正则表达式 3月日更

上线 Python 应用仅需一条命令的开源框架:Zappa(详细教程)

HelloGitHub

Python Serverless 无服务器云函数

Python基础之:函数

程序那些事

Python 数据分析 Python3 程序那些事

面试现场:小伙伴的美团一面分享和分析[含答案]

小傅哥

Java 面试 小傅哥

大括号之谜:C++的列表初始化语法解析

华为云开发者联盟

c++ 函数 语法 元素 std::array

阿里P8大牛亲自讲解!难道Android真的凉了?3面直接拿到offer

欢喜学安卓

android 程序员 面试 移动开发

Dubbo SPI 使用源码分析

Yangjing

dubbo spi ProxyFactory 服务暴露 ExtensionFactory

【笔记】第六周 - 第 2 课

Geek_娴子

拖延症这个毛病「Day 16」

道伟

28天写作

小喜量化交易系统开发|小喜APP软件开发

系统开发

阿里P8大佬亲自讲解!万字Android技术类校招面试题汇总,已拿offer

欢喜学安卓

android 程序员 面试 移动开发

IDEA 常用插件与配置

TroyLiu

Java vim ide idea插件 IntelliJ IDEA

二维码高端路线养成计

happlyfox

学习 技能 28天写作 3月日更

工作日志3-2-3

技术骨干

与前端训练营的日子 -- Week18

SamGo

学习

BI掌柜量化交易系统开发|BI掌柜炒币机器人软件APP开发

系统开发

了解MySQL6种约束的不同和特点

华为云开发者联盟

MySQL 数据 约束 主键约束 自增长约束

实现一个全链路监控平台很难吗?Pinpoint、SkyWalking、Zipkin,哪个实现比较好?

xcbeyond

架构 技术方案 链路监控 3月日更

快速排序算法实现及优化

Silently9527

Java 排序算法 快速排序

亿级用户中心的设计与实践

vivo互联网技术

大数据 架构设计 数据安全

Python 语言基础之 变量和常量

HoneyMoose

世界上最好的排序算法是什么?

Nick

数据结构 算法 快速排序

女神节·走近又美又飒的程序媛

华为云开发者联盟

华为 程序媛 IT 工程师 汉服

知识+AI融合创新探索,华为云论文被AI顶级学术期刊IEEE TPAMI接受

华为云开发者联盟

AI 模型 华为云 卷积神经网络 IEEE TPAMI论文

10 个解放双手超实用在线工具,有些代码真的不用手写

程序员小富

Java

数据分析师

Nydia

《精通比特币》学习笔记(第四章)

棉花糖

区块链 读书笔记 3月日更

MT马特量化交易系统开发|MT马特量化交易软件APP开发

系统开发

列表推导式与字典推导式,滚雪球学 Python

梦想橡皮擦

28天写作 3月日更

「架构师训练营 4 期」 第九周 - 001&2

凯迪

架构师训练营 4 期

如何实现JS真正意义上的弱引用?_大前端_Sathya Gunasekaran_InfoQ精选文章