news 2026/8/30 8:59:50

Twenty 内存持续增长时,如何用 Chrome DevTools 定位可疑对象

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Twenty 内存持续增长时,如何用 Chrome DevTools 定位可疑对象

Twenty 内存持续增长时,如何用 Chrome DevTools 定位可疑对象

【免费下载链接】twentyThe open alternative to Salesforce, designed for AI.项目地址: https://gitcode.com/GitHub_Trending/tw/twenty

如果你的 Twenty 页面用着用着越来越卡,很可能是 Twenty 内存泄漏,或者至少存在内存持续增长的问题。下面这套流程能帮你完成两件事:先用 Chrome DevTools 确认内存压力是真实存在的,而不是加载记录时的正常波动;再把堆里可疑的对象,映射回 Twenty 具体的前端模块或服务端场景,判断下一步该往哪里查。

一、先判断 Twenty 是否真的存在内存压力

在打开任何工具之前,先确认你要排查的现象属于哪一类。常见的信号有:

  • 同一个列表反复切换后,页面操作延迟越来越高;
  • 使用 AI 功能(提问、生成文档)时,会话时间越长响应越慢;
  • 标签页占用内存随使用时长一路向上,即使你什么也不做也不回落。

判断标准很简单:临时波动 vs 持续增长。把 Twenty 当作普通应用来观察——反复执行同类操作(进出列表、打开和关闭记录页、触发一次 AI 请求),打开任务管理器或 Chrome 的"性能"面板看这条曲线的终点。如果内存明显高于初始值,且静止等待数分钟后仍不下降,甚至继续爬升,才值得进入下面的取证流程。

Twenty 的记录详情页,是长会话中容易累积前端状态的一类典型场景

二、用 Chrome DevTools 采集一次有效证据

  1. 在 Twenty 页面按F12打开 DevTools,切到Memory面板。
  2. 先做一次基线操作:正常浏览几个页面,然后点Take heap snapshot保存第一份快照。
  3. 重复执行你怀疑的操作序列,比如连续打开并关闭 20 条记录、跑两次 AI 提问。
  4. 再点Take heap snapshot保存第二份快照。
  5. 在快照列表中右键第二份,选择Compare snapshots,直接查看两份快照之间的差异。

这里有两个概念需要分清:

  • Allocation sampling(分配采样):持续记录"哪些调用栈在分配内存",适合回答"是谁在不停造对象",开销小,适合长时间开着观察增长过程。
  • Heap snapshot(堆快照):某一时刻所有存活对象的完整清单,适合回答"现在都存着什么、被谁持有"。

对大多数排查来说,两次快照的对比比任何单次截图都有价值——差异表直接告诉你哪些构造函数、哪些模块新增了多少实例,而不需要你在几万行对象里自己找。

三、从内存对象反推 Twenty 中的可能位置

对比结果里重点看三列:

  • Constructor(构造函数):对象是谁造的。出现你熟悉的组件名、类名,就是直接线索。
  • Instances:实例数。关注它是否随你的操作次数近似线性增长。
  • Retained Size:释放这个对象能连带回收的总内存。它比单看对象大小或数量更重要——一个只有 1 KB 的引用对象,可能通过闭包拖着一整个 50 MB 的数据结构。

拿到可疑对象后,用Retainers(持有链)回答"它被谁攥着":

  1. 在 Retainers 里从右往左读,找到第一个你认识的名字:某个 React 组件、某个服务类、某个模块级变量。
  2. 如果持有者是闭包,看闭包所在的源码路径,它通常指向具体文件。
  3. 把名字带回项目代码里搜索,确认归属模块。

结合 Twenty 的代码结构,几条常见的映射线索:

  • 大量 UI 组件、订阅回调、事件监听堆积,通常指向 packages/twenty-front/src/modules/ 下的业务模块,例如object-recordviewssse-db-event这类与记录、视图、数据库事件流相关的目录。
  • AI 对话与执行相关的对象,对应前端ai模块,服务端在 packages/twenty-server/src/engine/metadata-modules/ai/。
  • 工作流相关的状态堆积,优先看前端workflow模块。

注意一个边界:DevTools 看的是浏览器里的 Twenty 前端;如果增长发生在服务端 Node 进程,需要换工具,但"对象被谁持有、随什么操作增长"的分析思路完全一样。

四、高频原因与对应的处理方向

1. 闭包或回调长期持有大对象

表现:组件已经卸载,对应的数据却还在堆里,Retainers 链条上挂着函数或监听器。

验证:在快照里展开该对象的 Retainers,看链条中是否有已"应该消失"的回调。

典型写法是这样的:

useEffect(() => { const onChunk = (c) => setItems((prev) => [...prev, c]); bus.on('chunk', onChunk); return () => bus.off('chunk', onChunk); }, []);

清理函数本身没问题,但每次setItems都生成新数组,而回调闭包始终持有容器——旧数据被间接锁住。优先检查组件卸载路径,以及任何会"整表重建"的更新逻辑。

2. 定时器与事件订阅未随生命周期清理

表现:长会话后某类对象数量缓慢爬升,重启页面立刻恢复。

验证:操作前后对比该类实例数;在代码里检索setIntervaladdEventListener,确认是否有对称的clearIntervalremoveEventListener

优先检查位置:定时轮询、SSE/数据库事件订阅(前端sse-db-event模块)、视图切换时的全局监听。

3. 状态或缓存无限增长

表现:某个数组或 Map 的条目数随使用时长单调上升,内容看起来都是"合理数据"。

验证:在快照中展开这个容器,看条目是不是越积越多、从不淘汰。

优先检查位置:模块级缓存、没有容量上限的前端状态容器。判断口诀——任何只进不出的集合,都值得怀疑

五、降低 Twenty 后续内存压力的实用做法

  • 给缓存设边界:容量上限或过期时间,让容器"可进可出",对应第 3 类问题。
  • 订阅类状态只存标识符,详情按需查询,避免把整份数据冗余进状态树。
  • 监听与定时任务随组件卸载成对移除,对应第 2 类问题;排查时在代码中搜addEventListenersetInterval是否都有对称清理。
  • 流式响应(如 AI 生成)的中间态及时收敛,不要逐条永久追加在内存数组里,对应第 1 类问题。
  • 大视图、重组件按路由拆分,进入页面才加载,从源头上控制初始驻留内存。

六、如何验收问题已经缓解

修完之后,用同样标准复测一轮:

  1. 重复执行相同操作序列,内存曲线能回到接近初始值,而不是每次都抬高一截;
  2. 之前线性增长的容器对象,实例数不再随使用时长持续增长;
  3. 长会话下页面交互延迟保持平稳,AI 请求的响应时间不再随使用时长明显变差。

三条都成立,说明这次内存问题已经收敛;否则回到第三节的 Retainers 分析,继续往持有链深处追。

【免费下载链接】twentyThe open alternative to Salesforce, designed for AI.项目地址: https://gitcode.com/GitHub_Trending/tw/twenty

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 8:58:11

STM32L4 UART DMA碰撞问题详解:原理、配置与排查实战

做嵌入式这些年,UART DMA这个组合我调试过很多次,每次觉得稳了,总会在新的芯片型号上翻车。最近在STM32L4上做电表通讯模块,遇到了一个非常典型的UART DMA collision问题:DMA接收看起来正常,但跑一段时间后…

作者头像 李华
网站建设 2026/8/30 8:56:02

算法服务故障复盘应留下什么

算法服务故障复盘应留下什么算法服务故障复盘应从可验证的事实开始:什么时候发现异常,哪些用户路径受影响,哪些指标和日志支持判断,采取了什么动作,以及恢复如何确认。不要用未经证实的规模、时长或单一“根因故事”替…

作者头像 李华
网站建设 2026/8/30 8:54:17

树莓派5上YOLOv8人员检测实战:基于OpenCV DNN与ONNX的轻量部署

这次我们继续树莓派5玩转AI系列,第三集的任务很明确:在树莓派5上部署YOLOv8,实现人员检测。不过这一集不走Ultralytics完整推理链路,而是用OpenCV的DNN模块加载ONNX模型,也就是标题里强调的“dnn版”。 为什么这么选&…

作者头像 李华