目錄
- 🔍 什么是內存泄漏(Memory Leak)?
- 🚨 常見的內存泄漏場景
- 1?? 未清除的定時器(setInterval / setTimeout)
- 2?? 全局變量(變量未正確釋放)
- 3?? 事件監聽未清除
- 4?? 閉包導致的內存泄漏
- 5?? DOM 引用未釋放
- 🛠 如何檢測和防止內存泄漏?
- 1?? 使用 Chrome DevTools 監測內存
- 2?? 使用 `WeakMap` 和 `WeakSet`
- 3?? 確保在 `useEffect` 里清理副作用(React)
- ? 總結
🔍 什么是內存泄漏(Memory Leak)?
內存泄漏 指的是 程序運行時,已經不再使用的內存無法被釋放,導致內存占用不斷增加,最終可能會導致應用性能下降甚至崩潰。
在 JavaScript 中,垃圾回收機制(GC, Garbage Collection) 會自動釋放不再使用的變量,但某些情況下,對象仍然被錯誤地引用,導致 GC 無法回收,從而造成內存泄漏。
🚨 常見的內存泄漏場景
1?? 未清除的定時器(setInterval / setTimeout)
當使用 setInterval
或 setTimeout
時,如果不手動清除,函數的引用會一直保留,導致內存泄漏。
function startTimer() {setInterval(() => {console.log("Running...");}, 1000);
}
// 調用后,即使不再需要,定時器仍然占用內存
startTimer();
? 解決方法:在組件銷毀或不需要時清除定時器
const timer = setInterval(() => {console.log("Running...");
}, 1000);clearInterval(timer); // 及時清除定時器
2?? 全局變量(變量未正確釋放)
如果一個變量被賦值到 window
或全局作用域,它將一直存在,導致內存無法被回收。
window.leak = []; // 這個數組永遠不會被回收
? 解決方法:避免在 window
作用域創建變量
(function() {let tempArray = [];
})();
3?? 事件監聽未清除
當事件監聽器(如 addEventListener
)綁定到 DOM 元素上,但該元素被移除時,監聽器仍然存在,導致 JavaScript 引用無法被釋放。
document.getElementById("btn").addEventListener("click", function() {console.log("Clicked!");
});
? 解決方法:組件卸載時移除監聽
const btn = document.getElementById("btn");
const handleClick = () => console.log("Clicked!");
btn.addEventListener("click", handleClick);// 在適當時機移除監聽器
btn.removeEventListener("click", handleClick);
4?? 閉包導致的內存泄漏
閉包 使得內部函數可以訪問外部函數的變量,但如果變量一直被引用,GC 無法釋放它。
function createClosure() {let data = new Array(1000000); // 大量數據return function () {console.log(data.length);};
}const closure = createClosure();
// `data` 變量不會被釋放
? 解決方法:在不需要時手動清空變量
let closure = createClosure();
closure = null; // 解除引用,讓 GC 回收
5?? DOM 引用未釋放
如果 JavaScript 變量仍然引用一個已刪除的 DOM 元素,該元素不會被回收。
let div = document.getElementById("myDiv");
document.body.removeChild(div); // 移除 DOM
// 但 div 變量仍然持有該元素的引用,導致泄漏
? 解決方法:手動釋放引用
div = null; // 解除 JavaScript 引用,讓 GC 處理
🛠 如何檢測和防止內存泄漏?
1?? 使用 Chrome DevTools 監測內存
- 打開
Performance
面板 → 錄制 → 檢查內存占用 - 在
Memory
面板 中使用 Heap Snapshot,查看哪些對象未被釋放。
2?? 使用 WeakMap
和 WeakSet
WeakMap
和WeakSet
不會阻止垃圾回收,適用于臨時數據存儲。
let weakMap = new WeakMap();
let obj = { key: "value" };
weakMap.set(obj, "some data");
obj = null; // `obj` 被回收,WeakMap 也自動釋放它的引用
3?? 確保在 useEffect
里清理副作用(React)
如果在 React 組件中添加 事件監聽、定時器 等,一定要在 useEffect
里清理:
useEffect(() => {const interval = setInterval(() => {console.log("Running...");}, 1000);return () => clearInterval(interval); // 組件卸載時清除定時器
}, []);
? 總結
- 內存泄漏 = 無用的對象無法被 GC 釋放,導致內存占用持續增長。
- 常見原因:未清理 定時器、事件監聽、閉包、DOM 引用、全局變量。
- 如何避免?
- 清除定時器和事件監聽 (
clearInterval, removeEventListener
) - 避免不必要的全局變量
- 正確管理閉包(在不需要時清空變量)
- 使用 Chrome DevTools 檢查內存泄漏
- 在 React 組件中使用
useEffect
清理副作用
- 清除定時器和事件監聽 (