症状
- 应用程序内存使用量持续增长
- 出现"JavaScript heap out of memory"错误
- 应用性能随时间推移而下降
- 服务器最终崩溃或重启
可能的原因
- 未正确关闭的事件监听器
- 全局变量持续增长
- 闭包引用导致的对象无法被垃圾回收
- 定时器未清除
解决方案
1. 使用内存分析工具
步骤:
- 使用Node.js --inspect参数启动应用
- 使用Chrome DevTools的Memory面板
- 或使用专业工具如heapdump、clinic.js等
- 分析内存快照找出泄漏源
代码示例:
node --inspect app.js
# 然后在Chrome中访问chrome://inspect
说明:
内存分析工具可以帮助识别哪些对象占用了大量内存,以及它们为什么没有被垃圾回收。
2. 正确管理事件监听器
步骤:
- 在不需要时移除事件监听器
- 避免在循环或频繁执行的函数中添加监听器
- 使用once选项或removeListener方法
代码示例:
// 添加监听器
element.addEventListener('click', handler);
// 移除监听器
element.removeEventListener('click', handler);
// 或使用once选项
element.addEventListener('click', handler, { once: true });
说明:
未移除的事件监听器是内存泄漏的常见原因,特别是在组件销毁或页面切换时。
3. 避免全局变量积累
步骤:
- 限制全局变量的使用
- 使用局部作用域
- 实现缓存过期和清理机制
代码示例:
// 不好的做法 - 全局缓存无限增长
const globalCache = {};
// 更好的做法 - 实现缓存限制
const LRUCache = require('lru-cache');
const cache = new LRUCache({
max: 500,
maxAge: 1000 * 60 * 60 // 1小时过期
});
说明:
全局变量中存储的数据不会被垃圾回收,如果持续增长会导致内存泄漏。
4. 清除定时器
步骤:
- 保存setTimeout和setInterval的返回值
- 在适当的时机调用clearTimeout或clearInterval
- 特别是在组件卸载或请求完成时
代码示例:
// 设置定时器
const timerId = setTimeout(() => {
// 执行操作
}, 1000);
// 清除定时器
clearTimeout(timerId);
说明:
未清除的定时器会阻止相关的回调函数及其闭包被垃圾回收,导致内存泄漏。
标签
node.js内存性能运行时