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

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

7254 次点击
所在节点    React
75 条回复
morenacl
25 天前
两个 list 不是同一个 list
NessajCN
25 天前
orderBy 定义呢?
UGas2t22Svnlm2K2
25 天前
这段代码存在潜在的内存泄漏风险,主要原因是 setInterval 和数据处理方式的结合导致的。以下是详细分析:
潜在的内存泄漏原因:
setInterval 和闭包:
setInterval 函数会创建一个新的计时器,每 300 毫秒重复调用 handleData() 函数。
每次调用 handleData() 都会创建一个闭包,闭包会捕获周围作用域中的变量引用,包括 data.value 、list 和 visibleData.value 。
数据累积:
当新的数据通过 websocket 订阅到达时,data.value 会更新,并调用 handleData() 函数。
在 handleData() 内部,会创建一个新的数组 list ,并将 data.value 的值赋给它。
然后对 list 进行排序,并将其赋值给 visibleData.value 。
然而,原始的 list 数组,它仍然持有对旧 data.value 的引用,并没有被显式清除或垃圾回收。
闭包引用:
每个由 setInterval 创建的闭包都保留了对旧 list 数组的引用,即使它不再需要了。
这就阻止了旧的 data.value 被垃圾回收,随着时间的推移导致内存累积。
解决方案:
在卸载时清除计时器:
确保在 onBeforeUnmount 中正确调用 clearInterval(timer) 来停止计时器,并防止进一步创建闭包。
避免不必要的数组创建:
不要在每次 handleData() 调用中创建新的 list 数组,可以考虑直接对 data.value 数组进行排序,或使用更高效的排序算法。
使用弱引用 (高级):
如果需要在闭包中维护引用而不导致内存泄漏,可以考虑使用 WeakMap 或 WeakSet 数据结构。这些结构持有弱引用,不会阻止垃圾回收。
注意: 使用弱引用需要仔细考虑对象生命周期和垃圾回收行为。
其他注意事项:
内存分析: 使用浏览器开发者工具或内存分析工具来跟踪内存使用情况并识别泄漏。
数据大小: 如果 data.value 数组包含大量数据,内存影响会更显著。考虑高效处理大型数据集的策略。
改进示例 (避免创建数组):
function handleData() {
const now = new Date().getTime();
data.value.forEach(item => {
const isTop = item.remindEndTime > now;
item.shine = isTop;
item.sort = isTop ? 0 : 1;
});
// 直接对 data.value 进行排序
data.value = orderBy(data.value, ['sort', 'updateTime'], ['asc', 'desc']);
visibleData.value = data.value;
}
yrk20212021
25 天前
import { onMounted, onBeforeUnmount } from 'vue'

// Assuming flightClient and WS_PREFIX are defined somewhere else

let timer = null;

function handleData() {
const now = new Date().getTime();
const newData = data.value.map(item => {
const isTop = item.remindEndTime > now;
return {
...item,
shine: isTop,
sort: isTop ? 0 : 1
};
});

// Sort only if there are changes in shine or updateTime
newData.sort((a, b) => {
if (a.shine !== b.shine) {
return a.shine ? -1 : 1;
}
return b.updateTime - a.updateTime;
});

visibleData.value = newData;
}

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

timer = setInterval(handleData, 300);
});

onBeforeUnmount(() => {
clearInterval(timer);
// Unsubscribe from WebSocket to prevent memory leaks
flightClient.unsubscribe(subscription);
});
ProxyXAI
25 天前
这可能是因为你的 `orderBy` 函数在排序时创建了新的数组,而原有的 `list` 数组并没有被垃圾回收。这可能是因为 `visibleData.value` 仍然引用着这个数组,或者其他地方也有对这个数组的引用。

你可以尝试在 `orderBy` 之后,手动将 `list` 设置为 `null`,或者使用 `list.length = 0` 清空数组,再设置 `visibleData.value = orderBy(...)`,这样可以确保 `list` 数组被垃圾回收。

另外,你的 `handleData` 函数并没有声明 `list` 为局部变量,这可能导致它成为全局变量,这也可能是内存泄露的原因。你应该使用 `let` 或 `const` 关键字声明 `list`。

示例代码:

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

记住,JavaScript 的垃圾回收是基于引用的。只要一个对象没有被引用,它就会被垃圾回收。但如果你的代码中有对这个对象的引用,那么它就不会被回收,即使你手动设置为 `null`。
yxd19
25 天前
flyqie
25 天前
@yxd19 #6

建议 @livid ,貌似还是不允许,只是不反馈就不查,要是 ai 生成后自己审阅过的貌似是允许的(因为这其实跟自己发区别不大了)。
lp4298707
25 天前
@NessajCN orderBy 就是 lodash 的 orderBy
wunonglin
25 天前
@Livid 这里都是 AI 回复鸭,麻了。
flyqie
25 天前
@flyqie #7

不过哪怕审阅过貌似也是默认允许,如果举报好像也是会处理的。
NessajCN
25 天前
@lp4298707 list 在哪儿定义的?
ProxyXAI
25 天前
@yxd19 @livid 我错了, 我只是使用 gpt-4 期望帮助一下这个用户, 后面粘贴 ai 的了
ProxyXAI
25 天前
后面不粘贴 AI
lp4298707
25 天前
@morenacl 我改成这种方式也是不行的. visibleData.value = orderBy(list, ['sort', 'updateTime'], ['asc', 'desc'])
xvxlb
25 天前
建议贴上完整代码
iosyyy
25 天前
visibleData.value 不还在吗 你只把 list = null 只改了 list 的指向 gc 扫的时候 visibleData.value 还在
按 java gc 答的 不大知道 js gc 啥样
iosyyy
25 天前
@iosyyy 也就是可达性分析还是会有 只是 list 不可达了不过 visibleData.value 还是可达的
freeman12
25 天前
建议放个复现链接
Livid
25 天前
@wunonglin 谢谢,那两个使用 AI 回复的账号已经被彻底 ban 。
Livid
25 天前
@flyqie 我没有时间看每天所有的新回复,但是如果收到报告,肯定会处理。

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

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

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

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

© 2021 V2EX