您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“JavaScript暫時性死區(qū)與垃圾回收機制是什么”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
我們來看一個例子
var tmp = 123; if (true) { tmp = 'abc'; console.log(tmp); let tmp; }
上面兩條語句都會報錯,因為初始化前無法訪問
但是我們知道var定義的變量,是存在變量提升的,我們來看一下其原理:
任何代碼運行前都會經(jīng)歷預(yù)編譯階段
,但它占用的時間往往極其短暫,所以我們一般感知不到,它主要是在內(nèi)存中開辟一些空間以此來存放變量與函數(shù)。
預(yù)編譯時,js引擎創(chuàng)建執(zhí)行上下文
,會將當(dāng)前作用域中的變量和函數(shù)聲明提升到頂部
而暫時性死區(qū)是一種對于變量提升的限制
當(dāng)一個變量被聲明時,在變量聲明前訪問該變量會拋出ReferenceError
異常。這種行為稱為暫時性死區(qū)
(TDZ,Temporal Dead Zone),存在于用let和const聲明的變量身上
本質(zhì)上是由于變量聲明被提升,但是變量的賦值操作不會被提升,但是又不會像var一樣給一個默認(rèn)的undefined,因此在變量聲明前訪問該變量會拋出異常,類似于C語言中使用沒有初始化的野指針,指針指向的堆或棧空間會暫時無法訪問
例如:
console.log(a); let a; //會報錯
說到垃圾回收機制,我們首先要了解什么是內(nèi)存泄漏
簡單來說,我們主機的內(nèi)存空間是有限的,內(nèi)存泄漏就是在運行程序時減少了我們可用的內(nèi)存,一般有用的內(nèi)存占用叫正常使用,而用過之后不需要留著的東西占著內(nèi)存空間卻不釋放,就叫內(nèi)存泄漏
在JavaScript中,內(nèi)存泄漏通常是由于以下幾個原因?qū)е碌模?/p>
全局變量
:沒有使用var、let或const關(guān)鍵字聲明的變量會被自動添加到全局對象中,如果意外地創(chuàng)建了全局變量,可能會導(dǎo)致這些變量無法被垃圾回收器釋放。
定時器或回調(diào)函數(shù)
:在創(chuàng)建定時器或回調(diào)函數(shù)時,如果沒有及時清除它們,可能會導(dǎo)致它們一直占用內(nèi)存空間,直到頁面關(guān)閉。
DOM節(jié)點引用
:在操作DOM節(jié)點時,如果保存了節(jié)點的引用,但是沒有及時釋放引用,可能會導(dǎo)致節(jié)點一直占用內(nèi)存空間,直到頁面關(guān)閉。
閉包
:在使用閉包時,如果閉包中引用了外部變量,但是沒有及時釋放閉包,可能會導(dǎo)致外部變量無法被垃圾回收器釋放。
循環(huán)引用
:在創(chuàng)建對象時,如果對象之間存在循環(huán)引用關(guān)系,可能會導(dǎo)致這些對象一直占用內(nèi)存空間,直到頁面關(guān)閉。
而JavaScript垃圾回收機制
就是使用自動內(nèi)存管理技術(shù),它會自動檢測哪些變量、對象和數(shù)據(jù)不再被使用,然后自動釋放它們所占用的內(nèi)存空間
那么它是如何實現(xiàn)的呢?一般有以下兩種算法:
引用計數(shù)
,它的基本思路是為每個對象維護一個引用計數(shù)器,當(dāng)一個對象被引用時,計數(shù)器加1,當(dāng)對象不再被引用時,計數(shù)器減1,當(dāng)計數(shù)器的值為0時,表示該對象不再被使用,可以被垃圾回收器釋放。引用計數(shù)算法可以快速地處理不再被引用的對象,但是它無法處理循環(huán)引用
的情況,因此在實際應(yīng)用中很少使用
。
標(biāo)記清除
,它的基本思路是通過標(biāo)記所有可以訪問到的對象
,然后清除所有未被標(biāo)記的對象
。在JavaScript中,垃圾回收器會從全局對象開始遍歷內(nèi)存中的所有對象,標(biāo)記所有可以訪問到的對象,然后清除所有未被標(biāo)記的對象。標(biāo)記清除算法可以有效地處理循環(huán)引用
的情況,但是它需要占用大量的時間和內(nèi)存空間
,因此在執(zhí)行過程中可能會出現(xiàn)性能問題。
基于此,v8引擎就對垃圾回收機制做了優(yōu)化
首先是分代垃圾回收
,將堆
分為新生代和老生代兩個區(qū)域,新生代中存儲的是生命周期較短的對象,而老生代中存儲的是生命周期較長的對象。新生代區(qū)域使用Scavenger
算法,老生代區(qū)域使用Mark-Sweep
(標(biāo)記清除
)和Mark-Compact
(標(biāo)記壓縮
)即標(biāo)記算法。
其次是增量式垃圾回收
,將整個垃圾回收的過程分為多個小步驟,在每個小步驟之間可以插入一些JavaScript代碼的執(zhí)行。這種方式可以避免垃圾回收造成的長時間的頁面卡頓。
最后是標(biāo)記壓縮Mark-Compact
,由于標(biāo)記清除Mark-Sweep
會清除未標(biāo)記的對象,導(dǎo)致只回收不連續(xù)的內(nèi)存塊,這樣還有有很多內(nèi)存塊碎片雖然被清除,仍無法使用。標(biāo)記壓縮法就是,V8引擎在老生代區(qū)域中,對標(biāo)記存活的對象進行遷移,再將移動后的存活對象的地址重新映射到新的位置,然后清除原地址內(nèi)存塊,這樣可用內(nèi)存塊就不會是碎片化,導(dǎo)致難以使用。但是移動對象的過程肯定也是影響性能的,不能過于頻繁。
再有就是在V8引擎中,垃圾回收的頻率是動態(tài)可變的,
V8引擎在啟動時設(shè)置的最大堆大小,一旦堆中的對象數(shù)量超過了閾值,V8引擎會立即啟動垃圾回收器。一般情況下,V8引擎是根據(jù)當(dāng)前堆中的對象數(shù)量、內(nèi)存使用情況、CPU占用率等來自動計算的垃圾回收間隔時間。
除自動調(diào)節(jié)垃圾回收頻率外,還可以通過手動觸發(fā)垃圾回收來調(diào)節(jié)垃圾回收的頻率。如在js中使用window.gc()方法手動觸發(fā)垃圾回收。
還可以在Node.js中可以通過--max-old-space-size參數(shù)來設(shè)置老生代堆內(nèi)存的閾值大小,通過--max-new-space-size參數(shù)來設(shè)置新生代閾值的大小
“JavaScript暫時性死區(qū)與垃圾回收機制是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。