在實際的場景中,如果觀察到內存持續出現峰值,并且內存消耗一直沒有減少,那可能存在內存泄漏。
使用 Chrome DevTools 來識別內存圖和一些內存泄漏,我們需要關注以下兩個方面:
● 使用性能分析器可視化內存消耗;
● 識別分離的 DOM 節點。
(1)使用性能分析器可視化內存消耗
以下面的代碼為例,
<!DOCTYPE html>
<html lang="en"><head><title>Memory leaks</title></head><body><button id="print">打印</button><button id="clear">清除</button></body>
</html>
<script>var longArray = [];function print() {for (var i = 0; i < 10000; i++) {let paragraph = document.createElement("p");paragraph.innerHTML = i;document.body.appendChild(paragraph);}longArray.push(new Array(1000000).join("y"));}document.getElementById("print").addEventListener("click", print);document.getElementById("clear").addEventListener("click", () => {window.longArray = null;document.body.innerHTML = "Cleared";});
</script>
有兩個按鈕:打印和清除。點擊“打印”按鈕,通過創建 paragraph 節點并將大字符串設置到全局,將1到10000的數字追加到DOM中。
“清除”按鈕會清除全局變量并覆蓋 body 的正文,但不會刪除單擊“打印”時創建的節點
當每次點擊打印按鈕時,JavaScript Heap都會出現藍色的峰值,并逐漸增加,這是因為JavaScript正在創建DOM節點并字符串添加到全局數組。當點擊清除按鈕時,JavaScript Heap就變得正常了。 除此之外,可以看到節點的數量(綠色的線)一直在增加,因為我們并沒有刪除這些節點。
(2)識別分離的 DOM 節點
當一個節點從 DOM 樹中移除時,它被稱為分離,但一些 JavaScript 代碼仍然在引用它。讓我們使用下面的代碼片段檢查分離的 DOM 節點。通過單擊按鈕,可以將列表元素添加到其父級中并將父級分配給全局變量。簡單來說,全局變量保存著 DOM 引用。
var detachedElement;
function createList(){let ul = document.createElement("ul");for(let i = 0; i < 5; i++){ul.appendChild(document.createElement("li"));}detachedElement = ul;
}
document.getElementById("createList").addEventListener("click", createList);
使用 heap snapshot 來檢查分離的DOM節點,可以在Chrome DevTools 的Memory面板中打開Heap snapshots選項:點擊下面藍色的Take snapshot按鈕,我們可以在中間的搜索欄目輸入Detached來過濾結果以找到分離的DOM節點,如下所示: