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

152 天前
 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']) 这段代码去掉 就没问题了

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

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
152 天前
RICKEYGONG
152 天前
@Livid ai 生成的
Livid
152 天前
@RICKEYGONG 47 楼的账号已经被彻底 ban 。
zhhbstudio
152 天前
Vue2 还是 Vue3 ,数据量大概多少
PS:Vue2 可以通过 composition-api 使用类似 Vue3 的 API
xiangyuecn
152 天前
你这点代码能泄露个啥,甩锅给 vue ,寿命+1 。
TKI
152 天前
wellerman
152 天前
1. setInterval 执行用了匿名函数,由于匿名函数定义在全局,可能会导致内存泄露

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

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

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

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

https://www.v2ex.com/t/1036400

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

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

© 2021 V2EX