亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

深入理解 JS 垃圾回收

2024-05-06 16:51:44
字體:
來源:轉載
供稿:網友

前言

JS之memoization,memoization 的原理是以參數作為 key,函數結果作為 value, 用對象進行緩存起來,以內存空間換 CPU 執行事件。memoization 的潛在陷阱即是嚴格意義的緩存有著完善的過期策略,而普通對象的鍵值對并沒有。

用閉包進行緩存的對象的內存空間,不會在函數執行完后被清除,在執行量大和參數多樣性的情況下,會造成內存占用且得不到釋放。

于是,本篇文章就來講講 JS 的垃圾回收。

JS 的垃圾回收機制的基本原理是:

找出那些不再繼續使用的變量,然后釋放其占用的內存,垃圾收集器會按照固定的時間間隔周期性地執行這一操作。

那我們怎么知道變量是不是在繼續使用呢?

首先,局部變量的生存周期是在函數聲明和執行階段,函數執行完畢后,局部變量就沒有存在的必要了。全局變量會在瀏覽器關閉或進程關閉才能釋放。

但還有一些場景,比如閉包,通過作用域鏈訪問到函數外部的自由變量,使得自由變量保存在內存中,不會隨著函數執行完畢而結束,以及對象的相互引用等,垃圾收集器就沒這么容易判斷哪個變量有用,哪個變量沒用了。

// 經典閉包function closure() {var name = "innerName";return function() {console.log(name);}}var inner = closure();inner(); // innerName;

所以,對于標識無用的變量的策略可能會實現不同,但目前在瀏覽器中,通常有兩種策略:標記清除和引用計數。

二、標記-清除(Mark-Sweep)

從2012年起,所有現代瀏覽器都使用了標記-清除垃圾回收算法, 那什么叫標記-清除呢?

當變量進入執行環境時,就標記這個變量為“進入環境”。當變量離開環境時,則將其標記為“離開環境”。從邏輯上講,永遠不能釋放進入環境的變量所占用的內存,因為只要執行流進入相應的環境,就可能會用到他們。

垃圾收集器在運行的時候會給存儲在內存中的所有變量都加上標記。

然后,它會去掉環境中的變量以及被環境中的變量引用的標記。而在此之后再被加上標記的變量將被視為準備刪除的變量,原因是環境中的變量已經無法訪問到這些變量了。

最后,垃圾收集器完成內存清除工作,銷毀那些帶標記的值,并回收他們所占用的內存空間。

另外,標記-清除有一個問題,就是在清除之后,內存空間是不連續的,即出現了內存碎片。如果后面需要一個比較大的連續的內存空間時,那將不能滿足要求。而標記-整理(Mark-Compact)方法可以有效地解決這個問題。標記階段沒有什么不同,只是標記結束后,標記-整理方法會將活著的對象向內存的一端移動,最后清理掉邊界的內存。

三、引用計數

另外一種不太常見的垃圾收集策略叫引用計數(Reference Counting),此算法把“對象是否不再需要”簡化定義為“對象有沒有其他對象引用到它”。如果沒有引用指向該對象(零引用),對象將被垃圾回收機制回收。
引用計數的策略是跟蹤記錄每個值被使用的次數,當聲明了一個變量并將一個引用類型賦值給該變量的時候這個值的引用次數就加 1,如果該變量的值變成了另外一個,則這個值得引用次數減 1,當這個值的引用次數變為 0 的時候,說明沒有變量在使用,這個值沒法被訪問了,因此可以將其占用的空間回收,這樣垃圾回收器會在運行的時候清理掉引用次數為 0 的值占用的內存。
而引用計數的不繼續被使用,是因為循環引用的問題會引發內存泄漏。

function problem() {var objA = new Object();var objB = new Object();objA.someObject = objB;objB.anotherObject = objA;}

objA 和 objB 通過各自的屬性相互引用,也就是說,兩個對象的引用次數都是 2。在函數執行完畢后,objA, objB 還將繼續存在,因為他們的引用計數永遠不會是 0。假如這個函數被多次執行,就會導致大量的內存得不到釋放。

四、NodeJs V8 中的垃圾回收機制

在 Node 中,通過 JS 使用內存時就會發現只能使用部分內存(64 位系統下約為 1.4 GB, 32 位系統下約為 0.7 GB),這導致 Node 無法直接操作大內存對象。

這是因為,以 1.5GB 的垃圾回收堆內存為例,V8 做一次小的垃圾回收需要 50 毫秒以上,做一次非增量式的垃圾回收要 1 秒以上,而垃圾回收過程會引起 JS 線程暫停執行這么多時間。因此,在當時的考慮下,直接限制堆內存是一個好的選擇。

那么,在這樣的內存限制下,V8 的垃圾回收機制又有什么特點?

4.1、內存分代算法

V8 的垃圾回收策略主要基于分代式垃圾回收機制,在 V8 中,將內存分為新生代和老生代,新生代的對象為存活時間較短的對象,老生代的對象為存活事件較長或常駐內存的對象。

JS,垃圾回收

V8 堆的整體大小等于新生代所用內存空間加上老生代的內存空間,而只能在啟動時指定,意味著運行時無法自動擴充,如果超過了極限值,就會引起進程出錯。

4.2 Scavenge 算法

在分代的基礎上,新生代的對象主要通過 Scavenge 算法進行垃圾回收,在 Scavenge 具體實現中,主要采用了一種復制的方式的方法—— Cheney 算法。

Cheney 算法將堆內存一分為二,一個處于使用狀態的空間叫 From 空間,一個處于閑置狀態的空間稱為 To 空間。分配對象時,先是在 From 空間中進行分配。

當開始進行垃圾回收時,會檢查 From 空間中的存活對象,將其復制到 To 空間中,而非存活對象占用的空間將會被釋放。完成復制后,From 空間和 To 空間的角色發生對換。

JS,垃圾回收

當一個對象經過多次復制后依然存活,他將會被認為是生命周期較長的對象,隨后會被移動到老生代中,采用新的算法進行管理。

還有一種情況是,如果復制一個對象到 To 空間時,To 空間占用超過了 25%,則這個對象會被直接晉升到老生代空間中。

4.3 標記-清除和標記-整理算法

對于老生代中的對象,主要采用標記-清除和標記-整理算法。標記-清除 和前文提到的標記一樣,與 Scavenge 算法相比,標記清除不會將內存空間劃為兩半,標記清除在標記階段會標記活著的對象,而在內存回收階段,它會清除沒有被標記的對象。
而標記整理是為了解決標記清除后留下的內存碎片問題。

4.4 增量標記(Incremental Marking)算法

前面的三種算法,都需要將正在執行的 JavaScript 應用邏輯暫停下來,待垃圾回收完畢后再恢復。這種行為叫作“全停頓”(stop-the-world)。

在 V8 新生代的分代回收中,只收集新生代,而新生代通常配置較小,且存活對象較少,所以全停頓的影響不大,而老生代就相反了。

為了降低全部老生代全堆垃圾回收帶來的停頓時間,V8將標記過程分為一個個的子標記過程,同時讓垃圾回收標記和JS應用邏輯交替進行,直到標記階段完成。

JS,垃圾回收

經過增量標記改進后,垃圾回收的最大停頓時間可以減少到原來的 1/6 左右。

五、內存泄漏

內存泄漏(Memory Leak)是指程序中己動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。

六、內存泄漏的常見場景

6.1 緩存

文章前言部分就有說到,JS 開發者喜歡用對象的鍵值對來緩存函數的計算結果,但是緩存中存儲的鍵越多,長期存活的對象也就越多,這將導致垃圾回收在進行掃描和整理時,對這些對象做無用功。

6.2 作用域未釋放(閉包)

var leakArray = [];exports.leak = function () {leakArray.push("leak" + Math.random());}

以上代碼,模塊在編譯執行后形成的作用域因為模塊緩存的原因,不被釋放,每次調用 leak 方法,都會導致局部變量 leakArray 不停增加且不被釋放。

閉包可以維持函數內部變量駐留內存,使其得不到釋放。

6.3 沒必要的全局變量

聲明過多的全局變量,會導致變量常駐內存,要直到進程結束才能夠釋放內存。

6.4 無效的 DOM 引用

//dom still existfunction click(){// 但是 button 變量的引用仍然在內存當中。const button = document.getElementById('button');button.click();}// 移除 button 元素function removeBtn(){document.body.removeChild(document.getElementById('button'));}

 

6.5 定時器未清除

// vue 的 mounted 或 react 的 componentDidMountcomponentDidMount() {setInterval(function () {// ...do something}, 1000)}

vue 或 react 的頁面生命周期初始化時,定義了定時器,但是在離開頁面后,未清除定時器,就會導致內存泄漏。

6.6 事件監聽為清空

componentDidMount() {window.addEventListener("scroll", function () {// do something...});}

同 6.5, 在頁面生命周期初始化時,綁定了事件監聽器,但在離開頁面后,未清除事件監聽器,同樣也會導致內存泄漏。

七、內存泄漏優化

1.解除引用

確保占用最少的內存可以讓頁面獲得更好的性能。而優化內存占用的最佳方式,就是為執行中的代碼只保存必要的數據。一旦數據不再有用,最好通過將其值設置為 null 來釋放其引用——這個做法叫做解除引用(dereferencing)

function createPerson(name){var localPerson = new Object();localPerson.name = name;return localPerson;}var globalPerson = createPerson("Nicholas");// 手動解除 globalPerson 的引用globalPerson = null;

解除一個值的引用并不意味著自動回收該值所占用的內存。解除引用的真正作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收。

2.提供手動清空變量的方法

var leakArray = [];exports.clear = function () {leakArray = [];}

3.在業務不需要用到的內部函數,可以重構在函數外,實現解除閉包
4.避免創建過多生命周期較長的對象,或將對象分解成多個子對象

5.避免過多使用閉包

6.注意清除定時器和事件監聽器

7.Nodejs 中使用 stream 或 buffer 來操作大文件,不會受 Nodejs 內存限制

8.使用 redis 等外部工具緩存數據

總結

JS 是一門具有自動垃圾收集的編程語言,在瀏覽器中主要通過標記清除方法來回收垃圾,NodeJs 中主要通過分代回收、Scavenge、標記清除、增量標記等算法來回收垃圾。在日常開發中,有一些不引人注意的書寫方式可能會導致內存泄漏,需要多注意自己的代碼規范。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美激情亚洲一区| 国产成人jvid在线播放| 欧美日韩一区二区免费在线观看| 日韩在线不卡视频| 国产亚洲精品一区二555| 青青精品视频播放| 欧美日韩成人在线观看| 欧美大尺度激情区在线播放| 日韩在线视频一区| 亚洲性日韩精品一区二区| 欧美中文在线免费| 91精品久久久久久| 国产不卡av在线| 欧美做受高潮电影o| 成人黄色影片在线| 国产成人久久久精品一区| 92看片淫黄大片欧美看国产片| 午夜精品久久久久久久99黑人| 91av网站在线播放| 日韩欧美国产中文字幕| 日韩在线不卡视频| 欧美一级高清免费播放| 国产精品久在线观看| 国产区精品在线观看| 日韩在线视频免费观看高清中文| 91视频国产高清| 播播国产欧美激情| 国内精品久久久久影院优| 中文欧美在线视频| 欧美日韩成人在线播放| 上原亚衣av一区二区三区| 日韩精品视频免费在线观看| yellow中文字幕久久| 国产免费久久av| 亚洲最大福利视频网| 国产欧美一区二区三区在线看| 国产精品午夜视频| 不卡中文字幕av| 国产精品扒开腿做爽爽爽男男| 91高清视频免费| 在线观看久久久久久| 亚洲成色999久久网站| 91精品国产免费久久久久久| 国产精品亚洲第一区| 91精品久久久久久综合乱菊| 日韩av影院在线观看| 午夜精品久久久久久久白皮肤| 精品亚洲一区二区三区在线播放| 久久久中精品2020中文| 在线精品视频视频中文字幕| 欧美日韩精品在线| 国产一区二区三区三区在线观看| 国产日韩精品电影| 精品高清一区二区三区| 成人黄色av播放免费| 久久精品成人欧美大片古装| 日韩视频在线观看免费| 久久国产精品首页| 青青久久aⅴ北条麻妃| 国产精品av网站| 91亚洲精华国产精华| 国产成人精品视频| 一区二区三区回区在观看免费视频| 中文字幕日韩欧美在线| 欧美精品videofree1080p| 国产欧美韩国高清| 成人a在线观看| 中文字幕在线亚洲| 亚洲综合在线中文字幕| 国内精品小视频在线观看| 欧美与欧洲交xxxx免费观看| 国产精品一区二区久久久| 精品国产自在精品国产浪潮| 久久久久久久一区二区三区| 欧美精品成人91久久久久久久| 91精品国产乱码久久久久久蜜臀| 久久免费精品日本久久中文字幕| 国产日韩av在线播放| 精品成人69xx.xyz| 久久久久99精品久久久久| 亚洲自拍偷拍在线| 一区二区在线视频播放| 欧美精品videossex88| 国产亚洲欧洲高清| 日产精品久久久一区二区福利| 亚洲成人av资源网| 蜜臀久久99精品久久久久久宅男| 成人午夜高潮视频| 欧美性xxxx极品hd满灌| 性色av香蕉一区二区| 亚洲精品国产品国语在线| 亚洲视频欧美视频| 日本a级片电影一区二区| 丝袜美腿亚洲一区二区| 国产欧美日韩精品专区| 7m第一福利500精品视频| 日韩免费黄色av| 久久777国产线看观看精品| 国产精品第七影院| 亚洲最大成人免费视频| 91精品视频在线播放| 亚洲欧洲av一区二区| 国产精品男人爽免费视频1| 性欧美xxxx视频在线观看| 欧美性受xxxx白人性爽| 91av在线播放| 欧美黄色片视频| 亚洲精品久久7777777| 91日本视频在线| 国产亚洲精品久久久久动| 日韩中文娱乐网| 国产精品久久久久久久电影| 97久久久久久| 欧美一级大片在线免费观看| 狠狠久久五月精品中文字幕| 日本久久中文字幕| 久久国产精品影视| 2021国产精品视频| 中文字幕v亚洲ⅴv天堂| 日韩精品在线观看视频| 国内精品久久久久影院优| 高清一区二区三区日本久| 岛国精品视频在线播放| 日韩在线中文视频| 欧美丰满老妇厨房牲生活| 亚洲欧美激情另类校园| 91精品国产电影| 欧美日韩视频在线| 91色在线视频| 国产一区红桃视频| 久久精品国亚洲| 亚洲综合一区二区不卡| 日韩福利在线播放| 在线日韩中文字幕| 在线观看国产成人av片| 亚洲国产小视频| 精品国产一区二区三区久久| 91视频88av| 欧美—级a级欧美特级ar全黄| 亚洲女人天堂视频| 国模精品一区二区三区色天香| 日韩欧美在线一区| 91精品综合久久久久久五月天| 一区二区三区动漫| 亚洲精品不卡在线| 亚洲精品91美女久久久久久久| 国产精品青青在线观看爽香蕉| 日韩激情av在线免费观看| 欧美视频一二三| 亚洲国产婷婷香蕉久久久久久| 欧美午夜视频一区二区| 欧美激情乱人伦| 久久这里只有精品99| 久久国产加勒比精品无码| 国产自产女人91一区在线观看| 欧美日韩国产丝袜美女| 日韩精品www| 欧美又大又硬又粗bbbbb| 国产又爽又黄的激情精品视频| 亚洲精品白浆高清久久久久久| 日韩中文字幕网址| 都市激情亚洲色图| 欧美猛少妇色xxxxx| 久久精品色欧美aⅴ一区二区|