這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 垃圾回收(Garbage Collection)是一種記憶體管理機制,用於檢測和清理不再被程式使用的記憶體,這些不再被使用的記憶體就被稱為垃圾。垃圾回收器會在 JS 引擎(瀏覽器或者 nodejs)內部周期性地運行,一般情況下無需開發者手 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前言
垃圾回收(Garbage Collection)是一種記憶體管理機制,用於檢測和清理不再被程式使用的記憶體,這些不再被使用的記憶體就被稱為垃圾。垃圾回收器會在 JS 引擎(瀏覽器或者 nodejs)內部周期性地運行,一般情況下無需開發者手動操作。
但是,瞭解垃圾回收機制的工作原理有助於我們寫出更加高效的 JS 代碼,使 JS 引擎更好的幫助我們完成垃圾回收,避免我們開發的應用出現記憶體泄漏問題。
垃圾是怎樣產生的?
JS 中的數據類型有原始類型和引用類型,原始類型占用的記憶體極小,一般是字元串、數字、布爾值這些,他們被存放在棧(stack)中。引用類型可以是數組、普通對象或者函數,他們一般會包含較多的數據,所以引用類型的實際數據存放在記憶體的堆(heap)中,然後在棧中會存放一個指向該實際數據的地址。
const str = "abc" // 原始類型 const obj = { foo: "bar" } // 引用類型
在 JS 中每聲明一個變數,該應用占用的記憶體就會相應的增加,但機器的記憶體是有限的,記憶體的占用不能無限制的增加。所以 JS 引擎需要適時的回收記憶體,釋放記憶體,保留性能,從而運行使程式流暢運行。
垃圾回收機制會回收哪些垃圾?
當一個對象不在任何引用被引用時,它就變得不可達,垃圾回收器就需要將這些不可達對象標記為可回收,併在適當的時機回收它們的記憶體。
以下麵這段代碼為例:
function myFunction() { const obj = { foo: "bar" } // do something } myFunction()
這段代碼中的 obj
對象在 myFunction
函數執行之後,就已經沒有被引用了,就需要被回收。
但當某個對象從開發角度上來說不再被使用了,卻意外的仍然在某個地方被引用,垃圾回收器就無法回收它的記憶體,就會造成記憶體泄漏(記憶體逐漸累積,程式占用的記憶體越來越多,當超過系統的可用記憶體時,就會造成程式崩潰)。
比如下麵這段代碼中 obj
對象就可能不會被回收:
function myFunction() { const obj = { foo: "bar" } setTimeout(() => { console.log(obj.foo) }, 1000) } myFunction()
因為 obj
作為閉包中的引用傳遞給了定時器的回調函數,即使 myFunction
執行完畢,由於定時器沒有被清除,obj
仍然被定時器回調函數持有引用,就可能導致 obj
不會被垃圾回收。
垃圾回收的演算法
JavaScript 中的垃圾回收機制主要基於以下兩個原則:
引用計數(Reference Counting)
每個對象都有一個引用計數,用於記錄有多少個引用指向該對象。當引用計數變為零時,表示沒有任何引用指向該對象,因此該對象可以被回收。
標記-清除(Mark and Sweep)
這是一種更常見的垃圾回收演算法。它從根對象(如全局對象、當前執行上下文中的變數等)開始,通過遍歷對象之間的引用關係,標記所有可達的對象。然後,回收器會清除未標記的對象,即不可達的對象,釋放其記憶體。
Chrome 和 nodejs 的垃圾回收演算法
Chrome 和 nodejs 都採用了谷_歌開源的 V8 引擎。V8 引擎的垃圾回收機制採用了標記清除演算法,但在此基礎又做了一些優化。
V8 引擎將記憶體分為新生代(Young Generation)和老生代(Old Generation)。大多數對象在新生代中創建,經過一定時間後,如果它們仍然存活,就會被晉升到老生代。
Scavenger 垃圾回收(新生代)
新生代使用了 Scavenger 垃圾回收演算法,它將記憶體劃分為一個存活區域和一個空閑區域。對象首先被分配到存活區域,當存活區域滿時,會執行垃圾回收操作,將存活的對象複製到空閑區域,並清空存活區域。
Mark-Sweep-Compact 垃圾回收(老生代)
老生代中使用了 Mark-Sweep-Compact(標記-清除-整理)垃圾回收演算法。它首先標記所有的存活對象,然後清除掉未被標記的對象,最後進行記憶體整理,使存活對象連續排列,減少記憶體碎片。
增量垃圾回收
V8 引擎還支持增量垃圾回收。他會將垃圾回收操作分成多個小步驟執行,每個步驟之間會插入一些 JavaScript 代碼的執行,從而避免長時間的垃圾回收造成的界面卡頓。
空閑時間垃圾回收
V8 引擎還在空閑時間執行部分垃圾回收操作,以充分利用閑置的計算資源。這些時間段可能是在程式等待用戶輸入、網路請求返回、或者其他暫時沒有任務需要處理的情況下出現的。
需要手動清除的記憶體
垃圾回收機制會根據演算法智能的回收大部分的記憶體,但由於業務邏輯的關係,它無法明確知道在我們的寫的(垃圾)代碼中,哪些對象其實是不再使用的,所以我們在開發過程中需要及時的清除不需要的事件監聽、定時器、計時器,避免迴圈引用,以及避免使用閉包。
清除事件監聽
const myButton = document.getElementById("myButton") function handleClick() { console.log("Button clicked!") } // 添加事件監聽器 myButton.addEventListener("click", handleClick) // 在頁面卸載或元素移除時解除事件監聽器 window.addEventListener("beforeunload", () => { myButton.removeEventListener("click", handleClick) })
執清除定時器、計時器
const timer = setTimeout(() => {}, 500) // 在頁面卸載或元素移除時解除事件監聽器 window.addEventListener("beforeunload", () => { clearTimeout(timer) })
手動調用垃圾回收
一般情況下我們無需手動調用垃圾回收,但有些瀏覽器支持主動觸發垃圾回收。
IE 瀏覽器
if (typeof window.CollectGarbage === "function") { window.CollectGarbage() }
Opera 瀏覽器
if (window.opera && typeof window.opera.collect === "function") { window.opera.collect() }