最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript垃圾回收機(jī)制實(shí)現(xiàn)方法

 更新時(shí)間:2025年08月22日 10:59:55   作者:超級(jí)無(wú)敵大蟑王  
垃圾回收的基本思路是確定哪些變量不會(huì)再次被使用,然后回收這些變量占用的內(nèi)存,本文給大家介紹JavaScript垃圾回收機(jī)制實(shí)現(xiàn)方法,感興趣的朋友跟隨小編一起看看吧

1.垃圾回收的概念

JavaScript使用垃圾自動(dòng)回收機(jī)制進(jìn)行內(nèi)存管理,無(wú)需程序員手動(dòng)分配和釋放內(nèi)存。垃圾回收的基本思路是確定哪些變量不會(huì)再次被使用,然后回收這些變量占用的內(nèi)存。垃圾回收過(guò)程是周期性的,垃圾回收程序每隔一段時(shí)間會(huì)運(yùn)行一次,垃圾回收也會(huì)影響到應(yīng)用程序的性能。常用的垃圾回收機(jī)制主要包括標(biāo)記清除和引用計(jì)數(shù)。

1.1 什么是垃圾回收機(jī)制:

GCGarbage Collection ,程序工作過(guò)程中會(huì)產(chǎn)生很多"垃圾",這些垃圾是程序不用的內(nèi)存或者是之前用過(guò)了,以后不會(huì)再用的內(nèi)存空間,而 GC 就是負(fù)責(zé)回收垃圾的,因?yàn)樗ぷ髟谝鎯?nèi)部,所以對(duì)于我們前端來(lái)說(shuō),GC 過(guò)程是相對(duì)比較無(wú)感的,這一套引擎執(zhí)行而對(duì)我們又相對(duì)無(wú)感的操作也就是常說(shuō)的 垃圾回收機(jī)制
不是所有語(yǔ)言都有 GC,一般的高級(jí)語(yǔ)言里面會(huì)自帶 GC,比如 Java、Python、JavaScript 等,也有無(wú) GC 的語(yǔ)言,比如 C、C++ 等,那這種就需要我們程序員手動(dòng)管理內(nèi)存了,相對(duì)比較麻煩
在像 C/C++ 這樣的語(yǔ)言中,開(kāi)發(fā)者需要手動(dòng)分配(malloc)和釋放(free)內(nèi)存。這種方式非常靈活,性能也高,但有兩個(gè)致命缺點(diǎn):

  • 忘記釋放:會(huì)導(dǎo)致內(nèi)存泄漏,程序占用的內(nèi)存會(huì)隨著時(shí)間推移越來(lái)越多,最終可能導(dǎo)致程序崩潰或系統(tǒng)變慢。
  • 提前釋放:或釋放了多次,會(huì)導(dǎo)致懸掛指針,當(dāng)程序嘗試訪問(wèn)一個(gè)已經(jīng)被釋放的內(nèi)存地址時(shí),會(huì)引發(fā)不可預(yù)知的錯(cuò)誤(通常是程序崩潰)。

JavaScript中存在兩種變量:局部變量全局變量。全局變量的生命周期會(huì)持續(xù)到頁(yè)面卸載;而局部變量聲明在函數(shù)中,它的生命周期從函數(shù)執(zhí)行開(kāi)始,直到函數(shù)執(zhí)行結(jié)束,在這個(gè)過(guò)程中,局部變量會(huì)在堆或棧中存儲(chǔ)它們的值,當(dāng)函數(shù)執(zhí)行結(jié)束后,這些局部變量不再被使用,它們所占有的空間就會(huì)被釋放。(不過(guò),當(dāng)局部變量被外部函數(shù)使用時(shí),其中一種情況就是閉包,在函數(shù)執(zhí)行結(jié)束后,函數(shù)外部的變量依然指向函數(shù)內(nèi)部的局部變量,此時(shí)局部變量依然在被使用,所以不會(huì)回收。)

2.垃圾回收機(jī)制是如何實(shí)現(xiàn)的

2.1核心理念:

GC 機(jī)制的核心思想是可達(dá)性。簡(jiǎn)單來(lái)說(shuō),就是判斷一個(gè)對(duì)象是否“可達(dá)”,如果不可達(dá),它就是“垃圾”。

  • 可達(dá)的對(duì)象:以某種方式被訪問(wèn)或使用的對(duì)象。
  • 不可達(dá)的對(duì)象:無(wú)法被訪問(wèn)到的對(duì)象,可以被安全地回收。

GC 會(huì)有一系列的對(duì)象,它們是可達(dá)性的起點(diǎn)。在 JavaScript 中,主要的根包括:

全局對(duì)象:比如瀏覽器環(huán)境下的 window 對(duì)象,Node.js 環(huán)境下的 global 對(duì)象。
因?yàn)槿謱?duì)象在應(yīng)用程序的整個(gè)生命周期內(nèi)都存在。只要你的網(wǎng)頁(yè)開(kāi)著,window 對(duì)象就永遠(yuǎn)不會(huì)消失。它是所有全局變量和內(nèi)置API(如 setTimeout, localStorage)的宿主。如果它被回收了,整個(gè)JavaScript環(huán)境就都將崩塌。因此,全局對(duì)象是GC最重要、最基礎(chǔ)的一個(gè)“根”。

函數(shù)調(diào)用棧:當(dāng)前正在執(zhí)行的函數(shù)中的局部變量和參數(shù)。
因?yàn)楫?dāng)前正在執(zhí)行的代碼和它所依賴的數(shù)據(jù),理所當(dāng)然是“存活”的。調(diào)用棧代表了程序執(zhí)行的“此時(shí)此刻”。如果這些正在使用的變量被回收了,程序?qū)⒘⒓闯鲥e(cuò)。因此,調(diào)用棧中所有棧幀里的變量和參數(shù),都被視為臨時(shí)的“根”。

活躍的 DOM 樹(shù):頁(yè)面上存在的 DOM 元素。
因?yàn)镈OM節(jié)點(diǎn)是構(gòu)成用戶界面的實(shí)體。用戶能看到、能與之交互的元素,必須始終存在于內(nèi)存中,瀏覽器需要依據(jù)它來(lái)進(jìn)行繪制和響應(yīng)事件。因此,所有在DOM樹(shù)上的節(jié)點(diǎn)都被認(rèn)為是“可達(dá)的”。

垃圾回收器會(huì)從這些“根”出發(fā),沿著引用鏈進(jìn)行遍歷。所有能從“根”訪問(wèn)到的對(duì)象,都會(huì)被認(rèn)為是“活”的(可達(dá)的);反之,所有無(wú)法從“根”訪問(wèn)到的對(duì)象,就會(huì)被認(rèn)為是“死”的(不可達(dá)的),并成為垃圾回收的目標(biāo)。

// 創(chuàng)建一個(gè)根對(duì)象,并被變量 user 引用
let user = {
  name: "Alice"
};
// 原來(lái)的 { name: "Alice" } 對(duì)象失去了引用,因此它變成了不可達(dá)對(duì)象,等待被回收。
user = null;

2.2主流垃圾回收算法

瀏覽器通常使用的垃圾回收方法有兩種:標(biāo)記清除引用計(jì)數(shù)。

2.2.1標(biāo)記清除

這是現(xiàn)代瀏覽器中最常用的垃圾回收算法。它完美地解決了循環(huán)引用的問(wèn)題。

  • 原理:分為兩個(gè)階段:
    1. 標(biāo)記階段:垃圾回收器從“根”對(duì)象開(kāi)始,遍歷所有可達(dá)的對(duì)象,并在這些對(duì)象上打上一個(gè)“標(biāo)記”,表示它們是存活的。
    2. 清除階段:垃圾回收器遍歷整個(gè)堆內(nèi)存,所有沒(méi)有被標(biāo)記的對(duì)象都被視為垃圾,并被回收,其占用的內(nèi)存被釋放。
  • 優(yōu)點(diǎn):可以解決循環(huán)引用的問(wèn)題。因?yàn)榧词?objAobjB 互相引用,但如果它們都無(wú)法從“根”訪問(wèn)到,那么它們就都不會(huì)被標(biāo)記,最終會(huì)被一起清除。
function createCircularReference() {
  let objA = {};
  let objB = {};
  objA.b = objB; 
  objB.a = objA; 
}
createCircularReference();
  • 缺點(diǎn)
    • 執(zhí)行效率問(wèn)題:GC 執(zhí)行時(shí),需要暫停整個(gè)程序的運(yùn)行,如果堆內(nèi)存很大,標(biāo)記和清除會(huì)很耗時(shí)。
    • 內(nèi)存碎片化:清除后,會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片。如果之后需要分配一個(gè)大對(duì)象,可能會(huì)因?yàn)闆](méi)有足夠大的連續(xù)空間而失敗。

2.2.2引用計(jì)數(shù)

這是早期的一種 GC 算法,思想非常簡(jiǎn)單。

  • 原理:為每個(gè)對(duì)象維護(hù)一個(gè)“引用計(jì)數(shù)器”。當(dāng)有一個(gè)引用指向該對(duì)象時(shí),計(jì)數(shù)器加1;當(dāng)引用被移除時(shí),計(jì)數(shù)器減1。當(dāng)計(jì)數(shù)器變?yōu)?時(shí),表示該對(duì)象不再被需要,可以被回收。
  • 優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,垃圾可以被立即回收,不會(huì)有“暫停”的感覺(jué)。
  • 致命缺點(diǎn):無(wú)法處理循環(huán)引用。
    看下面的例子:

這種情況下,就要手動(dòng)釋放變量占用的內(nèi)存:

obj1.a =  null
obj2.a =  null

2.3 V8 引擎的優(yōu)化:分代回收

為了解決標(biāo)記-清除算法的效率問(wèn)題,Google 的 V8 引擎(用于 Chrome 和 Node.js)采用了一種更先進(jìn)的策略:分代回收。
這個(gè)策略基于一個(gè)重要的觀察:“大部分對(duì)象都是朝生夕死的”。也就是說(shuō),很多對(duì)象在創(chuàng)建后很快就不再被使用,而少數(shù)對(duì)象會(huì)存活很長(zhǎng)時(shí)間。

V8 將堆內(nèi)存分為兩個(gè)主要區(qū)域:
新生代:Scavenge 算法

  • 特點(diǎn):存放生命周期短的對(duì)象,空間較?。ㄍǔ?1-8MB),垃圾回收頻繁且速度快。
  • 內(nèi)部結(jié)構(gòu):新生代內(nèi)存被平分為兩個(gè)相等的空間:From 空間(使用中)To 空間(空閑)
  • **回收過(guò)程:
    1. 新對(duì)象首先被分配在 From 空間。
    2. 當(dāng) From 空間快要被占滿時(shí),觸發(fā)一次新生代的 GC。
    3. GC 會(huì)檢查 From 空間中的存活對(duì)象,并將它們復(fù)制到 To 空間。
    4. 復(fù)制完成后,F(xiàn)rom 空間剩下的所有對(duì)象都是垃圾。整個(gè) From 空間被一次性清空。
    5. From 空間和 To 空間的角色互換,等待下一次 GC。
  • 晉升:如果一個(gè)對(duì)象在新生代中經(jīng)過(guò)了多次 Scavenge 依然存活,那么它被認(rèn)為是生命周期較長(zhǎng)的對(duì)象,會(huì)被“晉升”到老生代中。此外,如果復(fù)制一個(gè)對(duì)象到 To 空間時(shí),To 空間的使用率超過(guò)了25%,該對(duì)象也會(huì)被直接晉升到老生代。
    老生代:標(biāo)記-清除與 標(biāo)記-整理
  • 特點(diǎn):存放生命周期長(zhǎng)或體積大的對(duì)象,空間較大,GC 頻率較低
  • 回收過(guò)程
    1. 主要使用標(biāo)記-清除算法,流程如前所述。
    2. 為了解決內(nèi)存碎片化問(wèn)題,V8 引入了標(biāo)記-整理算法。它在標(biāo)記階段之后,不是直接清除垃圾,而是將所有存活的對(duì)象向內(nèi)存的一端移動(dòng),然后直接清理掉邊界之外的所有內(nèi)存。這樣就得到了連續(xù)的空閑空間。

3.減少垃圾回收

3.1 手動(dòng)處理:
雖然瀏覽器可以進(jìn)行垃圾自動(dòng)回收,但是當(dāng)代碼比較復(fù)雜時(shí),垃圾回收所帶來(lái)的代價(jià)比較大,所以應(yīng)該盡量減少垃圾回收。

  • 對(duì)數(shù)組進(jìn)行優(yōu)化: 在清空一個(gè)數(shù)組時(shí),最簡(jiǎn)單的方法就是給其賦值為[ ],但是與此同時(shí)會(huì)創(chuàng)建一個(gè)新的空對(duì)象,可以將數(shù)組的長(zhǎng)度設(shè)置為0,以此來(lái)達(dá)到清空數(shù)組的目的。
  • 對(duì)對(duì)象進(jìn)行優(yōu)化: 對(duì)象盡量復(fù)用,對(duì)于不再使用的對(duì)象,就將其設(shè)置為null,盡快被回收。

避免意外的全局變量
始終使用 constlet 聲明變量,開(kāi)啟嚴(yán)格模式('use strict';)。

function leakyFunction() {
  // 如果沒(méi)有 'let' 或 'const', a 會(huì)被創(chuàng)建為全局變量
  // 它將永遠(yuǎn)不會(huì)被回收,除非手動(dòng)設(shè)為 null
  a = new BigObject();
}

警惕閉包
閉包是 JavaScript 的強(qiáng)大特性,但也很容易造成內(nèi)存泄漏。閉包可以使其父函數(shù)中的變量在函數(shù)執(zhí)行結(jié)束后仍然存活。

function createClosure() {
  let largeData = new Array(1e6).fill('*'); 
  // 這個(gè)返回的函數(shù)持有了對(duì) largeData 的引用
  return function() {
  	...
    return largeData.length;
  };
}
let myClosure = createClosure();
// 即使 createClosure 執(zhí)行完畢,largeData 也不會(huì)被回收,因?yàn)樗?myClosure 引用。
// 如果不再需要它,應(yīng)手動(dòng)解除引用。
myClosure = null;

定時(shí)器和事件監(jiān)聽(tīng)器
setInterval, setTimeoutaddEventListener 如果不被正確清理,它們的回調(diào)函數(shù)和其引用的外部變量都不會(huì)被回收。

let element = document.getElementById('my-button');
let largeData = new BigObject();
function onClick() {
  // do something with largeData
}
element.addEventListener('click', onClick);
// 正確做法:
// element.removeEventListener('click', onClick);
// element = null;

在組件銷(xiāo)毀或元素移除時(shí),務(wù)必使用 clearInterval, clearTimeoutremoveEventListener 清理掉相關(guān)的定時(shí)器和監(jiān)聽(tīng)器。

面試標(biāo)準(zhǔn)答案(背下來(lái)?。?br />Q:請(qǐng)簡(jiǎn)述 JavaScript 的垃圾回收機(jī)制。

A:
JavaScript 的垃圾回收(GC)是自動(dòng)內(nèi)存管理機(jī)制,核心是??標(biāo)記清除??和??引用計(jì)數(shù)??。

  • ??標(biāo)記清除??:從根對(duì)象(如全局變量)出發(fā),標(biāo)記所有可達(dá)對(duì)象,清除未標(biāo)記的內(nèi)存。??優(yōu)點(diǎn)??是解決循環(huán)引用問(wèn)題,??缺點(diǎn)??是執(zhí)行時(shí)可能短暫卡頓。
  • 引用計(jì)數(shù)??:記錄每個(gè)對(duì)象的引用次數(shù),歸零時(shí)立即回收。??缺點(diǎn)??是無(wú)法處理循環(huán)引用,可能導(dǎo)致內(nèi)存泄漏。

V8 引擎的優(yōu)化:

  • 分代回收:新生代(存活時(shí)間短)用Scavenge算法,老生代用標(biāo)記清除/整理。
  • 增量回收:將GC任務(wù)拆分成小任務(wù),減少卡頓。。

避免內(nèi)存泄漏:

  • 避免意外全局變量。
  • 及時(shí)清除定時(shí)器、事件監(jiān)聽(tīng)。
  • 謹(jǐn)慎使用閉包引用大對(duì)象。
  • 清理無(wú)用的 DOM 引用。

到此這篇關(guān)于JavaScript垃圾回收機(jī)制的文章就介紹到這了,更多相關(guān)js垃圾回收機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

石狮市| 高陵县| 临安市| 惠州市| 渝北区| 米易县| 垣曲县| 曲靖市| 子洲县| 商南县| 中牟县| 邹平县| 遂昌县| 澎湖县| 水城县| 漠河县| 玛纳斯县| 宜宾市| 依兰县| 依兰县| 玉山县| 泾阳县| 贵南县| 青浦区| 望谟县| 荥经县| 湘潭市| 扎囊县| 景宁| 将乐县| 五大连池市| 富宁县| 长海县| 石城县| 泗水县| 连平县| 封开县| 阜康市| 长白| 孟州市| 黑河市|