写代码的时候,总觉得内存够用,直到程序跑着跑着越来越卡,最后直接崩溃。这时候打开任务管理器一看,内存占用一路飙升,八成是碰上了堆内存泄露。
啥叫堆内存泄露?
简单说,就是程序申请了内存,用完没还回去。操作系统以为你还得用,一直不敢回收,时间一长,大量内存被白白占着,系统就扛不住了。
最常见的几个原因
1. 忘记释放动态分配的内存
C/C++ 里用 new 或 malloc 申请内存,但忘了用 delete 或 free 释放。比如这段代码:
int* ptr = new int[1000];
ptr = new int[500]; // 原来的内存直接丢了,再也找不回来
第二行重新赋值,第一块内存的地址没了,彻底泄漏。
2. 对象引用没清空
Java、JavaScript 这类带垃圾回收的语言也不是绝对安全。只要对象还被引用着,GC 就不会动它。比如全局数组不断往里塞对象,从不清空:
const cache = [];
setInterval(() => {
cache.push({ data: '每次加一个,永远不删' });
}, 100);
// 几分钟后,内存爆炸
3. 事件监听没解绑
前端开发常犯这个错。给 DOM 元素绑了事件,元素删了,监听还在。
function addListener() {
const btn = document.getElementById('btn');
btn.addEventListener('click', handleClick);
}
// 后来 btn 被 remove 掉了,但监听函数还挂在那,相关作用域也释放不了
4. 循环引用
两个对象互相引用,形成闭环,垃圾回收机制识别不了,干脆就不收。常见于老版本 IE 的 DOM + JS 组合:
var elem = document.getElementById('myDiv');
elem.ref = elem; // 自己引用自己
5. 定时器或回调堆积
setInterval 没清理,每次执行又产生新数据,越积越多。比如轮询接口没控制频率,也没在组件销毁时清除:
let intervalId = setInterval(() => {
fetchData().then(res => {
store.push(res); // 数据只进不出
});
}, 1000);
// 页面切换后 intervalId 没 clear,store 越来越大
怎么发现和排查
Chrome 开发者工具的 Memory 面板就能拍快照对比。Node.js 可以用 heapdump + Chrome DevTools 分析。C++ 程序推荐 valgrind 或 AddressSanitizer。
平时写代码多留个心眼:new 了有没有配对 delete,事件绑了有没有解,定时器开了有没有关。别让小疏忽拖垮整个系统。