为啥这段代码会造成内存泄露啊?

2024-04-28 15:05:11 +08:00
 lp4298707

function handleData() {
  list = data.value;
  const now = new Date().getTime()
  list.forEach(item => {
    const isTop = item.remindEndTime > now
    item.shine = isTop;
    item.sort = isTop ? 0 : 1;
  })
  // 闪烁的放最前 再以更新时间排序
  list = orderBy(list, ['sort', 'updateTime'], ['asc', 'desc'])
  visibleData.value = list
  list = null
}

onMounted(() => {
  flightClient.subscribe(WS_PREFIX + '/xxx/xxx', res => {
    data.value = res
    handleData()
  })

  timer = setInterval(() => {
    handleData()
  }, 300)
})

onBeforeUnmount(() => {
  clearInterval(timer)
})

每次调用我都把 list 置为空了 为什么还是会导致内存蹭蹭涨?

如果把 list = orderBy(list, ['sort', 'updateTime'], ['asc', 'desc']) 这段代码去掉 就没问题了

11346 次点击
所在节点    React
75 条回复
chairuosen
2024-04-28 17:39:15 +08:00
你 onBeforeUnmount 没有 unsubscribe ,会不会是这个,组件如果是每次父级刷新全新创建,就有问题了
asasjajsajsd
2024-04-28 17:45:22 +08:00
不是,我有点奇怪,就不能后端给你干这个事情么,数据库排序很快啊
ysc3839
2024-04-28 17:48:47 +08:00
提一下,js 取时间戳可以直接写 +new Date()
ysc3839
2024-04-28 17:56:12 +08:00
@ysc3839 刚刚去测了一下性能,发现 Date.now()性能最好,+new Date()性能最差,new Date().getTime()排在中间。
edward1987
2024-04-28 17:57:32 +08:00
神奇,不好复现啊主要,OP 看下能不能整个最小复现代码放出来看下。
wawaguo
2024-04-28 17:58:18 +08:00
来自 Code Copilot 的回答:
wawaguo
2024-04-28 17:58:32 +08:00
这段代码的内存泄露可能由多种因素引起。我会逐一分析可能的原因,并提供解决方案:

1. 闭包中的变量未被释放
闭包(比如在 setInterval 、forEach 或事件订阅回调中定义的函数)常常会意外捕获并持久保持一些变量,尤其是在你反复设置定时器或不断订阅事件时。如果这些闭包保持对外部变量(如 list )的引用,这些变量就不会被垃圾回收。

解决方案:
确保在不需要时解除对变量的引用,或者在组件卸载时清除所有相关的订阅和定时器。

2. 定时器未正确清除
在代码中,onBeforeUnmount 用来清除定时器,但如果因为某种原因这个生命周期钩子没有被正确调用(或者定时器的引用 timer 被意外修改),定时器将继续运行,从而可能导致内存泄露。

解决方案:
确保 timer 变量不会在其他地方被误修改。
在组件的生命周期中确保 onBeforeUnmount 正确调用。
3. Websocket 订阅未取消
如果你的 Websocket 订阅在组件卸载时没有被取消,订阅中的回调函数可能持续持有一些变量的引用,从而导致内存泄露。

解决方案:
在 onBeforeUnmount 钩子中添加取消 Websocket 订阅的逻辑。

4. 数据绑定导致的内存泄露
如果使用了双向数据绑定或响应式数据系统(如 Vue 、React 状态),并且数据更新频率非常高,可能会导致旧数据对象未能及时回收。

解决方案:
减少不必要的数据变动。
手动触发垃圾回收(这种方式并不推荐,因为它依赖于具体的 JavaScript 引擎实现)。
5. 高频度的 DOM 操作或重渲染
高频的更新可能导致 DOM 重绘或组件重渲染,如果处理不当,旧的 DOM 节点或组件实例可能无法被垃圾回收。

解决方案:
使用虚拟 DOM 或有效的重渲染策略来优化渲染过程。
确保在更新数据前,不必要的 DOM 元素已被正确清除或重用。
检查和调试方法:
使用浏览器的开发者工具中的内存分析工具。
检查定时器和事件监听器是否都被正确清理。
观察内存使用情况,看是否随时间持续增长。
通过这些分析和调整,你应该能够识别并修复代码中的内存泄露问题。如果需要具体的代码修改建议,我可以帮助进一步修改。
RICKEYGONG
2024-04-28 18:01:28 +08:00
RICKEYGONG
2024-04-28 18:03:07 +08:00
@Livid ai 生成的
Livid
2024-04-28 18:06:04 +08:00
@RICKEYGONG 47 楼的账号已经被彻底 ban 。
zhhbstudio
2024-04-28 18:43:31 +08:00
Vue2 还是 Vue3 ,数据量大概多少
PS:Vue2 可以通过 composition-api 使用类似 Vue3 的 API
xiangyuecn
2024-04-28 18:56:37 +08:00
你这点代码能泄露个啥,甩锅给 vue ,寿命+1 。
TKI
2024-04-28 19:41:18 +08:00
wellerman
2024-04-28 19:45:26 +08:00
1. setInterval 执行用了匿名函数,由于匿名函数定义在全局,可能会导致内存泄露

按 #4 楼给的方法,直接调用命名函数:
timer = setInterval(handleData, 300);

2. 防止 orderBy( 处理时间超过 300ms

var working = false;
function handleData() {
if (working) {
return;
}
working = true;
...
working = false;
}
lulinchuanllc
2024-04-28 20:54:03 +08:00
flightClient.subscribe 在卸载时也应该 off 掉吧,否则这里的变量都被消息订阅列表里的订阅函数缓存了
lp4298707
2024-04-28 21:53:01 +08:00
@TKI 我运行这个看浏览器资源管理器也是占用了 1.4 个 G,你的没有这个情况吗?
DOLLOR
2024-04-29 01:53:00 +08:00
建议把 data 和 visibleData 都改成浅响应( shallowRef )。
我推测 orderBy 在处理 list 的时候,因为 vue 的响应式处理造成了过重的负担。
visper
2024-04-29 09:47:21 +08:00
这里代码应该没有什么内存泄露,猜测可能是因为 timeout 300 毫秒执行频率太高,然后数组又太大,orderby 里面每次又生成一个新的 list 重新给 vue 再重新处理初始化响应。造成浏览器内存回收没这么及时。
coderHu
2024-04-29 09:47:32 +08:00
现在解决了么?蹲个解决方案
ColdBird
2024-04-29 09:52:32 +08:00
如果 visibleData.value 不是 null ,说明 list 不是原来的 list 了,不然一定是 null
话说这代码写的也有点抽象

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1036400

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX