V8 內存分配與垃圾回收
作者:一像素
www.cnblogs.com/onepixel/p/7422820.html
V8 將內存空間主要分為:新生代 和 老生代 兩種 。
1、新生代空間
新生代空間中的對象為存活時間較短的對象,大多數的對象被分配在這裡,這個區域很小但是垃圾回特別頻繁 。
它將堆內存一分為二,每一部分空間稱為 semispace,其中一個處於使用狀態(from 空間),另一個處於閑置狀態(to 空間)
對於新產生的對象,將從 from 空間中分配內存 。
新生代分配內存非常容易,我們只需要保存一個指向內存區的指針,不斷根據新對象的大小進行遞增即可。當該指針到達了新生代內存區的末尾,就會觸發一次垃圾回收。
新生代的垃圾回收採用Scavenge演算法 ,其工作原理如下:
首先檢查 from 空間,將存活對象複製到 to 空間,非存活對象將會被釋放。完成複製後,from 空間和 to 空間角色發生轉換。新產生的對象始終從 from 空間中分配內存,to 空間則處於閑置狀態。當再次進行垃圾回收時,也會執行和第一次同樣的操作,如果存在以下兩種情況,存活對象就會被複制到老生代空間中,這個過程稱為對象晉陞。
存活對象已經經歷過一次 Scavenge 回收 。
to 空間內存佔用比例超過 25% (保證下次新對象有足夠的空間可分配)
2、老生代空間
老生代空間中的對象為存活時間長或常駐內存對象,大多數從新生代晉陞的對象會被移動到這裡。
老生代佔用內存較多,如果使用 Scavenge演算法,不僅會浪費一半空間,複製如此大塊的內存消耗時間將會很長,所以 Scavenge 演算法顯然不適合。
V8 對於老生代中的垃圾回收,採用 Mark-Sweep (標記清除) 和 Mark-Compact(標記整理) 相結合 。
(1) Mark-Sweep
Mark-Sweep 分為 標記 和 清除 兩個階段 。
在標記階段需要遍歷堆中的所有對象,並標記那些活著的對象,然後進入清除階段。在清除階段,只清除沒有被標記的對象。由於標記清除只清除死亡對象,而死亡對象在老生代中佔用的比例很小,所以效率較高。
標記清除存在的問題是,進行一次標記清除後,內存空間往往是不連續的,會出現很多的內存碎片。如果後續需要分配一個需要內存空間較多的對象時,如果所有的內存碎片都不夠用,將會使得V8無法完成這次分配,提前觸發垃圾回收。
(2) Mark-Compact
標記整理正是為了解決標記清除所帶來的內存碎片的問題。標記整理在標記清除的基礎進行修改,將其的清除階段變為緊縮極端。在整理的過程中,將活著的對象向內存區的一段移動,移動完成後直接清理掉邊界外的內存。緊縮過程涉及對象的移動,所以效率並不是太好,但是能保證不會生成內存碎片。
3、三種回收策略比較
從圖中可以看出,在 Mark-Sweep 和 Mark-Compact 之間,由於 Mark-Compact 需要移動對象,所以它的執行速度最慢。
所以在取捨上,V8 主要使用 Mark-Sweep,在空間不足以對新生代中晉陞過來的對象進行分配時才使用 Mark-Compact 。
4、垃圾回收引起的性能問題
為了避免出現 JavaScript 應用邏輯 與 垃圾回收操作 產生不一致的衝突,垃圾回收的三種基本演算法都需要將應用邏輯暫停下來,待垃圾回收完成後,再恢復執行應用邏輯,這種行為被稱為全停頓 。
按官方說法,以 1.5G 的垃圾回收堆內存為例,V8 做一次小的垃圾回收需要 50ms 以上,做一次非增量式垃圾回收甚至需要 1s 以上。這是垃圾回收中引起的 JavaScript 線程暫停執行時間,在這樣的時間花銷下,應用性能和響應能力都會直線下降。
在 V8 的分代式垃圾回收中,一次小垃圾回收只收集新生代,由於新生代默認配置的較小,且其中活動對象通常較少,所以即便它是全停頓,影響也不大。
但 V8 的老生代通常配置較大,且存活對象較多,全堆垃圾回收的標記、清理、整理等動作造成的停頓就會比較嚴重。
為降低全堆垃圾回收而導致的停頓時間,V8 做了以下改善措施:
(1) 限制堆內存大小
新生代:64 位系統 和 32 位系統分別為 32M 和 16 M (from 和 to 空間各佔一半)
老生代:64 位系統 和 32 位系統分別為 1400M 和 700 M
(2) 增量式垃圾回收
V8 先從標記階段入手,將原來一口氣停頓完成的動作改為 增量標記(Incremental Marking),也就是拆分為許多小步進,每做完一步進,就讓 JavaScript 應用邏輯執行一小會兒,垃圾回收與應用邏輯交替執行,直到標記階段完成。V8 後續還引入 Lazy Sweep(延遲清除)、Incremental Compaction (增量式整理),讓清理與整理動作也變成增量式的。同時還計劃引入並行標記與並行整理,進一步利用多核性能來降低每次停頓的時間。
5、垃圾回收的觸發條件
作用域: 能形成作用域的函數調用、with 語句 以及 全局作用域。
閉包: V8 無法主動回收內存中的閉包引用和全局變數引用。
6、內存泄漏
通常,造成內存泄漏的原因有如下幾個:
隊列消費不及時
作用域未釋放
參考文獻:
[1] 朴靈,深入淺出 Node.js,人民郵電出版社,2013
覺得本文對你有幫助?請分享給更多人
關注「前端大全」,提升前端技能


※2000塊都沒人的蘋果SE
※原來炸雞隻是副業!肯德基竟然推出了這麼多好玩的黑科技產品
※高性能 React:3 種提升 APP 速度的新工具
※Threejs 開發 3D 地圖實踐總結
※你一定要知道的 Chrome DevTool 新功能
TAG:前端大全 |
※JVM內存分配、GC原理與垃圾收集器
※廚餘垃圾回收
※G1 垃圾收集器之對象分配過程
※分類垃圾與混合垃圾將差別化收費
※1秒回收、5秒到賬,一桶收以智能垃圾回收機切入再生資源回收市場
※能夠從電子垃圾中回收黃金的細菌,預計能獲價值300億美元的黃金
※垃圾收集概念
※日本式垃圾分類VS中國式垃圾分類
※變廢為寶——垃圾的回收利用
※CMS垃圾收集器
※垃圾佬的1300元吃雞配置
※垃圾佬的1300元吃雞配置
※幾十台電腦,全是32位win7配8G內存條,莫非這就是垃圾佬的天堂嗎
※垃圾回收率達80% 荷蘭加速發展循環經濟
※幾十台電腦,全是32位系統配8G內存條,這就是垃圾佬的天堂嗎
※痛心,火箭8000萬高帥富真沒法用了,整整5分鐘垃圾時間卻不敢投3分
※DNF:肥宅160W開出13鑽石強化券 幾分鐘後卻直言垃圾遊戲,含淚卸載
※從垃圾分類處理到研究垃圾
※車內只要3分鐘,垃圾灰塵都清空!
※6塊錢包郵買了個MP3,還贈送皮套布袋,什麼時MP3成了垃圾價?