一文詳解Javascript內(nèi)存機制與垃圾回收
一 內(nèi)存機制
1.1 數(shù)據(jù)類型
Javascript 是一種動態(tài)的(在運行過程中檢查數(shù)據(jù)類型,)、弱類型(同一個變量可以保存不同類型的數(shù)據(jù))的語言。
Javascript 數(shù)據(jù)類型一共有 8 種: Boolean,Null,Undefined,Number,BigInt,String,Symbol,Object。前 7 種數(shù)據(jù)類型為原始類型
,Object
為引用類型
。兩種類型的數(shù)據(jù)在內(nèi)存中存放的位置不同。
1.2 內(nèi)存空間
Javascript 在執(zhí)行的過程中,主要有三種類型的內(nèi)存空間,分別是:代碼空間、??臻g、堆空間。
代碼空間主要是存儲可執(zhí)行代碼的。
原始數(shù)據(jù)類型存儲在??臻g中, 引用數(shù)據(jù)類型存儲在堆空間中。
說明示例:
function foo(){ var a = "極客時間" var b = a var c = {name:"極客時間"} var d = c } foo()
??臻g
,也就是之前提起的調(diào)用棧
,用來存儲執(zhí)行上下文。
上述代碼執(zhí)行到第 3 行的時候,變量 a 和 b 的值都直接保存在執(zhí)行上下文中,執(zhí)行上下文又被壓入棧空間中,所以可以理解為變量 a 和 b 都存放在棧空間中。
當(dāng)執(zhí)行到第 4 行,Javascript 引擎判斷變量 c 的值是引用類型,Javascript 引擎將該值分配到堆空間里,分配后該值會有一個在堆空間的地址,然后將該地址賦值給變量 c。第 5 行,將 c 賦值給 d,實際是將引用地址賦值給了 d,修改引用類型對象的值,改的是堆空間中數(shù)據(jù)的值。
??臻g因為要維護執(zhí)行上下文,影響程序的執(zhí)行效率,所以空間一般比較小,可以用來存放原始類型的小數(shù)據(jù);引用類型的數(shù)據(jù)可以很大,所以堆空間比較大,不過堆空間分配內(nèi)存和回收內(nèi)存會占用一定的時間。
??臻g切換執(zhí)行上下文狀態(tài):
1.3 閉包內(nèi)的數(shù)據(jù)存儲
閉包內(nèi)的變量存儲到??臻g還是堆空間?
說明示例:
function foo() { var myName = "極客時間" let test1 = 1 const test2 = 2 var innerBar = { setName:function(newName){ myName = newName }, getName:function(){ console.log(test1) return myName } } return innerBar } var bar = foo() bar.setName("極客邦") bar.getName() console.log(bar.getName())
執(zhí)行上述代碼,當(dāng) foo 函數(shù)執(zhí)行完之后,調(diào)用棧中的 foo 函數(shù)的執(zhí)行上下文會被銷毀,由于變量 myName 和 test1 持續(xù)存在外部引用,Javascript 引擎判斷這是一個閉包,會在堆空間中創(chuàng)建一個closure(foo)
的對象,這個對象中包含這兩個被引用的變量。
二 垃圾回收
一些數(shù)據(jù)在使用后就不再需要了,這部分數(shù)據(jù)繼續(xù)存在內(nèi)存里,并且逐漸積累,會占用越來越多的空間,通過垃圾回收機制以釋放有限的內(nèi)存空間。
2.1 不同語言的垃圾回收策略
一般,垃圾回收分為手動回收
和自動回收
兩種策略。
以 C / C++ 為例,使用的是手動回收策略,內(nèi)存的分配與銷毀都是由程序員寫的代碼控制的,如果不主動銷毀垃圾數(shù)據(jù),將導(dǎo)致內(nèi)存泄漏。
以 Javascript / Java 為例,垃圾數(shù)據(jù)由內(nèi)置的垃圾回收器自動回收,不需要通過編寫代碼手動釋放。
2.2 調(diào)用棧的垃圾回收機制
Javascript 引擎把執(zhí)行上下文壓入調(diào)用棧的同時,使用一個記錄當(dāng)前執(zhí)行狀態(tài)的指針(ESP)指向當(dāng)前的執(zhí)行上下文,表示正在執(zhí)行該執(zhí)行上下文。
當(dāng)該執(zhí)行上下文運行完畢,ESP 下移,這個下移的操作就是銷毀被執(zhí)行過的上下文的過程。
示例說明:
function foo(){ var a = 1 var b = {name:"極客邦"} function showName(){ var c = 2 var d = {name:"極客時間"} } showName() } foo()
2.3 堆空間垃圾回收機制
2.3.1 代際假說
代際假說觀點認為,大部分對象在內(nèi)存中有用的時間很短,一經(jīng)分配內(nèi)存,很快就變得不可訪問;少量數(shù)據(jù)持續(xù)活躍,活的很久。
2.3.2 V8 垃圾回收機制:分代收集
基于代際假說,V8 把堆分為老生代
和新生代
,新生代中存放的是生存時間短的對象(1—8M空間),老生代中存放的是生存時間久的對象(空間比較大)。
針對新生代和老生代,V8 使用不用的回收機制,以便更高效的進行垃圾回收。使用主垃圾回收器
回收老生代的垃圾數(shù)據(jù),使用副垃圾回收器
回收新生代的垃圾數(shù)據(jù)。
垃圾回收原理
- 標記空間中的活動對象(還在使用的對象)和非活動對象(可以回收的對象)。
- 標記完成之后,統(tǒng)一清理內(nèi)存中所有被標記的可回收對象,回收這部分對象占據(jù)的內(nèi)存。
- 內(nèi)存整理:頻繁回收對象之后,內(nèi)存中存在大量不連續(xù)空間(內(nèi)存碎片),如果要分配較大連續(xù)內(nèi)存時,可能出現(xiàn)內(nèi)存不足的情況,所以需要將內(nèi)存碎片成連續(xù)的空間。
副垃圾回收器
主要負責(zé)新生代的垃圾回收。新生代空間較小,里面的對象一般較??;回收頻繁。
回收機制采用 Scavenge 算法。該算法把新生代空間劃半分為對象區(qū)域
和空閑區(qū)域
。
新加入的對象存到對象區(qū)域,當(dāng)對象區(qū)域快被寫滿時,執(zhí)行一次垃圾回收。在回收過程中,對對象區(qū)域里的對象做標記,標記完成后,把存活的對象復(fù)制到空閑區(qū)域中并有序的排列起來(消除內(nèi)存碎片),把無用的對象清理掉。
復(fù)制完成后,對象區(qū)域和空閑區(qū)域進行角色翻轉(zhuǎn),對象區(qū)域變成空閑區(qū)域,空閑區(qū)域變成對象區(qū)域。這樣這兩塊區(qū)域可以無限重復(fù)使用下去。
復(fù)制操作需要時間成本,因此為了執(zhí)行效率,新生區(qū)的空間一般設(shè)置的比較小。
經(jīng)過兩次垃圾回收依然存活的對象,會被移動到老生區(qū)中,以防止過多的存活對象擠滿新生區(qū)。
主垃圾回收器
主要負責(zé)老生區(qū)的垃圾回收。老生區(qū)的里的對象一般占用空間大(因復(fù)制操作耗時所以不適合使用 Scavenge 算法),存活的時間比較長。
回收機制采用的是 標記-清除(Mark-Sweep)算法。從一組根元素開始,遞歸遍歷這組根元素,在遍歷過程中能到達的元素稱為活動對象,沒有達到的元素判斷標記為垃圾數(shù)據(jù)。
標記完成之后,執(zhí)行清除過程。
如上圖,清除后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片;為了避免內(nèi)存碎片,進化出了另一種算法:標記-整理(Mark-Compact),標記完成后,讓所有存活的對象移向內(nèi)存一端,然后直接清理掉端邊界外的數(shù)據(jù)。
全停頓
Javascript 是運行在主線程上的(單線程),一旦執(zhí)行垃圾回收算法,其他 Javascript 腳本將被阻塞,需要等待垃圾回收完畢才能繼續(xù)執(zhí)行。這種行為叫做全停頓。
如上圖,過長的全停頓將導(dǎo)致頁面卡頓。新生代因其空間小存活對象占用空間小,對全停頓影響不大,老生代的垃圾回收是造成全停頓的主因。為了降低老生代垃圾回收造成的卡頓,V8 將標記過程分為一個個子標記,讓子標記和 Javascript 腳本交替進行,這個解決方案叫做增量標記算法。
以上就是一文詳解Javascript內(nèi)存機制與垃圾回收的詳細內(nèi)容,更多關(guān)于Javascript內(nèi)存機制與垃圾回收的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于js?+?html2canvas實現(xiàn)網(wǎng)頁放大鏡功能
最近接到任務(wù),需實現(xiàn)【網(wǎng)頁】放大鏡的效果,百度搜索?【js?放大鏡】關(guān)鍵字,千篇一律的都是一些仿淘寶/京東等電商網(wǎng)站中查看規(guī)格大圖的效果實現(xiàn),根本無法滿足我的需求,于是自己花了點時間調(diào)研實現(xiàn),在這里分享給大家,感興趣的朋友可以參考下2023-12-12前端接口報錯Required?request?body?is?missing解決辦法
這篇文章主要給大家介紹了關(guān)于前端接口報錯Required?request?body?is?missing的解決辦法,文中通過代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-12-12