Relay 数据驻留与垃圾回收指南:理解 presence-of-data、Query Retention 与 GC 策略
前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载Relay 在客户端内存 Store 中缓存查询结果以便在无需网络请求的情况下复用数据、快速渲染页面。但数据不会永远驻留Relay 会通过垃圾回收Garbage Collection删除不再被任何组件引用的数据。本文基于 Relay 官方指南presence-of-data.md展开系统讲解数据何时存在于 Store、何时被回收以及如何通过environment.retain、gcScheduler、gcReleaseBufferSize精确控制数据的保留时长帮助你在“复用缓存”与“控制内存”之间找到平衡。数据何时存在缓存的生命周期理解 Relay 缓存复用的第一步是弄清楚数据在 Store 中的生命周期lifetime——即数据是否存在于 Store 中以及它会存在多久。基本规律如下查询首次被获取后该查询及其变量对应的数据会写入 Relay Store只要该查询正在屏幕上被渲染其数据一般会持续存在从未被获取过的查询其数据自然缺失于 Store 中。也就是说数据的存在与否首先取决于“是否被获取过”其次取决于“是否仍在被使用”。但这里有一个不可避免的矛盾随着应用不断获取不同的查询Store 中的数据会无限累积变得过大、过陈旧stale。为了控制内存占用Relay 运行一个名为**垃圾回收Garbage Collection**的进程删除不再使用的数据。设计要点垃圾回收的存在与“复用缓存”的目标天然存在张力。如果数据被过早删除后续再次复用时就会落空导致必须重新发起网络请求才能渲染界面。因此如何让想要复用的数据在需要的时间内保持缓存是本节乃至整条 reusing-cached-data 指南线的核心命题。Relay 的垃圾回收机制Relay 对本地内存 Store 执行垃圾回收的方式是删除任何不再被应用中任何组件引用的数据。从实现层面看这一机制由 RelayModernStore 承担关键逻辑如下每个查询operation在 Store 内部对应一个root entry记录在_roots一个Map中并维护一个引用计数refCount见 RelayModernStore.js#L115-L123调用environment.retain()会使对应 root entry 的refCount加一释放dispose时减一见 RelayModernStore.js#L383-L440当refCount降到 0 时该查询的数据进入“可被回收”状态——但如果配置了 release buffer会先进入缓冲期GC 的实际执行由_collect()生成器与_gcStep()分步驱动通过gcScheduler调度见 RelayModernStore.js#L854-L886。值得注意的细节即使某个查询从未被显式 retain只要gcReleaseBufferSize 0且 release buffer 未满查询数据在写入时也会被临时记入_roots并放入 release buffer以便近期内复用见_recordSourceOperationRelayModernStore.js#L606-L634。:::note 通常你不需要操心垃圾回收与数据保留的配置——这些应由应用基础设施在RelayEnvironment层面统一配置。本文内容供理解与参考属于“知道底层如何运转”的知识储备。 :::Query Retention手动保留查询数据保留retain一个查询是向 Relay 表明该查询及其变量对应的数据不应被垃圾回收删除。多个调用方可以同时保留同一个查询只要至少有一个调用方还在保留该查询的数据就不会从 Store 中被删除。默认行为组件挂载期间的自动保留默认情况下任何使用useQueryLoader/usePreloadedQuery及其他相关 API 的查询组件会在自身挂载期间自动保留所渲染的查询。组件卸载后它们会**释放release**该查询——这意味着此后任意时刻该查询的数据都可能被删除。这套自动保留逻辑对应 Store 中 root entry 引用计数的增减组件挂载时计数加一卸载时计数减一归零即进入可回收状态。在组件生命周期之外保留environment.retain如果需要在组件的生命周期之外继续保留某个查询可以使用environment.retain(operationDescriptor)操作// 保留查询这将阻止 Relay 对 // 该查询及其变量的数据执行垃圾回收 const disposable environment.retain(queryDescriptor); // 释放 disposable 将放开对该查询及其变量数据的保留 // 意味着如果它没有被其他地方保留 // 就可能被 Relay 的垃圾回收随时删除 disposable.dispose();如上所述这样可以做到即使查询组件已卸载仍继续保留查询数据让其他组件或未来再次挂载的同一组件能够复用这些保留的数据。完整示例从 graphql 标签到 OperationDescriptorenvironment.retain接收的是 Relay 内部的操作描述符OperationDescriptor而不是裸的graphql模板字符串。需要先用getRequest获取请求描述符再通过createOperationDescriptor绑定具体变量const { createOperationDescriptor, getRequest, graphql, } require(relay-runtime); // graphql 查询对象 const query graphql...; // 构造 Relay 内部的查询表示 const queryRequest getRequest(query); const queryDescriptor createOperationDescriptor( queryRequest, variables, ); // 保留查询这将阻止 Relay 对 // 该查询及其变量的数据执行垃圾回收 const disposable environment.retain(queryDescriptor); // 释放 disposable 将放开对该查询及其变量数据的保留 // 意味着如果它没有被其他地方保留 // 就可能被 Relay 的垃圾回收随时删除 disposable.dispose();更完整的用法可参考 retaining-queries.md其中同样强调Relay 会根据挂载的查询组件自动管理查询数据的保留因此产品代码中通常不需要直接调用retain。对于高级或特殊场景查询数据的保留应由基础设施层代码如 Router统一处理。适用场景典型的例子是路由级代码——当用户离开某个页面、组件卸载后Router 可以在导航层面保留该页面对应的查询数据使得用户返回时能瞬间渲染而无须等待网络请求。控制 Relay 的垃圾回收策略目前可以在创建 Relay Store 时提供2 个选项来控制垃圾回收行为。它们的真实签名可在 RelayModernStore 构造函数 中确认。GC Scheduler何时执行 GCgcScheduler是一个可传给 Relay Store 的函数用于决定一次 GC 执行应该被调度到何时运行// 示例调度函数 // 接收一个回调并将其调度到未来的某个时间执行 function gcScheduler(run: () void) { resolveImmediate(run); } const store new Store(source, {gcScheduler});要点默认行为如果不提供gcSchedulerRelay 会使用resolveImmediate来调度 GC。resolveImmediate是基于 Promise 的setImmediate替代方案将回调放到微任务队列中尽快执行见 resolveImmediate.js自定义策略可以提供自己的调度函数让 GC 比默认行为更不激进例如基于时间、基于 scheduler 优先级或其他启发式规则约定按惯例实现不应立即执行回调即不应同步调用run而应将其延后到合适的时机。源码中的调度链路为scheduleGC()创建_gcRun生成器后调用gcScheduler(_gcStep)_gcStep每执行完一步若未完成会再次调用gcScheduler调度下一步从而把一次完整 GC 分摊到多次调度中见 RelayModernStore.js#L854-L886。此外在乐观更新optimistic update期间可通过holdGC()挂起 GC待恢复后再执行。GC Release Buffer Size释放缓冲大小Relay Store 内部维护一个释放缓冲区release buffer用于在查询被其原始持有者释放后默认发生在渲染该查询的组件卸载时仍然临时保留指定数量的查询。这使得在用户返回之前访问过的页面、标签页或内容时更有可能且更容易复用缓存数据。配置方式是在创建 Store 时指定gcReleaseBufferSizeconst store new Store(source, {gcReleaseBufferSize: 10});要点缓冲区大小为 0等价于没有释放缓冲区查询一旦被释放就会被立即回收默认大小环境的 release buffer 大小为10。源码层面的对应关系RelayModernStore.js#L169-L170this._gcReleaseBufferSize options?.gcReleaseBufferSize ?? DEFAULT_RELEASE_BUFFER_SIZE; // 10缓冲区的运作机制RelayModernStore.js#L642-L651被释放refCount为 0的 root entry 会被推入_releaseBuffer当缓冲区长度超过gcReleaseBufferSize时最旧最早加入的条目被逐出evict并从_roots中删除随后调度 GC因此缓冲区越大越多的最近访问过的查询得以保留复用命中率越高但同时内存占用也越大——这是需要在内存与复用率之间权衡的配置。测试用例describe(GC with a release buffer, ...)明确验证了该行为例如“在 caller 释放后仍将数据保留在 release buffer 中”“如果数据不过期即使只发布数据也会被保留在 release buffer 中”见 RelayModernStore-test.js 附近的测试组。这从侧面印证release buffer 正是“页面/标签往返复用”的核心机制。实践建议综合官方指南与源码实现可总结出如下实践建议默认不动 GC 配置默认的gcSchedulerresolveImmediate与gcReleaseBufferSize 10已覆盖大多数场景。GC 与保留策略应由应用基础设施Environment 层、Router 层统一配置产品代码一般无需干预需要跨页面复用数据时用 retain若某个查询的数据在组件卸载后仍需被复用如返回上一页、切换标签页在 Router 等基础设施层调用environment.retain()并在适当时机dispose()是最直接、最可控的方式权衡 release buffer 大小增大gcReleaseBufferSize可提升近期访问数据的复用命中率但会增加内存占用设为 0 则关闭缓冲、立即回收适合对内存敏感的场景自定义 gcScheduler 控制回收时机若 GC 过于频繁导致卡顿可按时间或优先级延后调度注意约定上不要同步执行回调。延伸阅读缓存复用总览reusing-cached-data/introduction.md数据是否过期staleness-of-data.md获取策略store-or-network等fetch-policies.md手动保留查询的完整 APIretaining-queries.md核心实现RelayModernStore.js、resolveImmediate.js行为验证测试RelayModernStore-test.js、RelayModernEnvironment-Retain-test.js赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐Relay 数据驻留与垃圾回收全指南理解 Presence of Data、Query Retention 与 GC 策略Relay 数据驻留与垃圾回收全指南理解 Presence of Data、Query Retention 与 GC 策略 导读 本指南聚焦 React Re前端开发工具Relay 缓存数据的存续性理解 Relay 垃圾回收、Query Retention 与 GC 策略配置Relay 缓存数据的存续性理解 Relay 垃圾回收、Query Retention 与 GC 策略配置 Relay 的核心设计之一是内置的规范化缓存no前端开发工具Relay 缓存数据的存在性与垃圾回收掌握 Query Retention 与 GC 配置Relay 缓存数据的存在性与垃圾回收掌握 Query Retention 与 GC 配置 在 Relay 中复用缓存数据是一个高频且容易踩坑的主题只前端开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

statsmodels.othermod 模块实战指南:用 BetaModel 对单位区间连续因变量建模(Beta 回归)

statsmodels.othermod 模块实战指南:用 BetaModel 对单位区间连续因变量建模(Beta 回归)

数据分析数据科学科研 【免费下载链接】statsmodels Statsmodels: statistical modeling and econometrics in Python 项目地址: https://gitcode.com/gh_mirrors/st/statsmodels 点击查看 免费下载 statsmodels.othermod 是 statsmodels 中专门容纳"难以归入…

2026/9/23 21:49:43 阅读更多 →
棋类博弈引擎开发:从规则建模到高效搜索实现

棋类博弈引擎开发:从规则建模到高效搜索实现

简介:本资源是面向计算机专业学生、人工智能初学者及算法竞赛备赛者的计算机博弈系统性入门讲义,由东北大学机器博弈研究室出品,聚焦博弈原理、软件实现与多棋类实战分析。内容覆盖博弈树搜索、Alpha-Beta剪枝、蒙特卡罗树搜索等核心算法&…

2026/9/23 21:49:43 阅读更多 →
Talos Linux SideroLinkConfig 配置指南:连接 SideroLink API 的机器配置文档详解

Talos Linux SideroLinkConfig 配置指南:连接 SideroLink API 的机器配置文档详解

云原生操作系统容器编排 【免费下载链接】talos Talos Linux is a modern Linux distribution built for Kubernetes. 项目地址: https://gitcode.com/gh_mirrors/ta/talos 点击查看 免费下载 SideroLinkConfig 是 Talos Linux 中用于建立 SideroLink 连接的机器配…

2026/9/23 21:48:42 阅读更多 →

最新新闻

Windows系统安装全指南:从U盘启动盘制作到UEFI/GPT分区方案

Windows系统安装全指南:从U盘启动盘制作到UEFI/GPT分区方案

不管是给老电脑续命,还是给新装的机器做首次引导,Windows系统的安装都属于那种“看着简单,做起来全是细节”的活儿。我前前后后帮同事、朋友装了不下几十台机器,自己也因为手贱删错分区、改了引导方式导致安装失败过好多次&#x…

2026/9/24 0:00:20 阅读更多 →
齿轮箱故障诊断中的传递路径分析:原理、Matlab实现与工程应用

齿轮箱故障诊断中的传递路径分析:原理、Matlab实现与工程应用

前阵子有朋友拿来一组齿轮箱振动数据,说频谱图上能看到好几个啮合频率边带,但就是说不清振动到底是从啮合点直接传出来的,还是先传到轴承、再经过箱体共振放大出来的。这个问题其实特别典型——齿轮箱故障诊断里,传感器只能装在箱…

2026/9/24 0:00:20 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
水下生物目标检测实战:YOLO工程与PyTorch训练推理全流程解析

水下生物目标检测实战:YOLO工程与PyTorch训练推理全流程解析

简介:面向水下生物目标检测场景,这份基于Python与PyTorch的深度学习资源包,整合了YOLO模型训练与推理所需的数据集、脚本及预训练权重,适合有一定深度学习基础、希望快速上手目标检测项目的开发者。资源共1830个文件,压…

2026/9/23 23:59:18 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →