欧美亚洲中文,在线国自产视频,欧洲一区在线观看视频,亚洲综合中文字幕在线观看

      1. <dfn id="rfwes"></dfn>
          <object id="rfwes"></object>
        1. 站長(zhǎng)資訊網(wǎng)
          最全最豐富的資訊網(wǎng)站

          聊聊V8的內(nèi)存管理與垃圾回收算法

          本篇文章帶大家了解一下V8引擎的內(nèi)存管理與垃圾回收算法,希望對(duì)大家有所幫助!

          聊聊V8的內(nèi)存管理與垃圾回收算法

          眾所周知,JS是自動(dòng)管理垃圾回收的,開(kāi)發(fā)者不需要關(guān)心內(nèi)存的分配與回收。而且垃圾回收機(jī)制在前端面試中也是??嫉牟糠?。本文主要講解V8的分代垃圾回收算法,希望閱讀本文后的小伙伴能夠?qū)?code>V8垃圾回收機(jī)制有個(gè)痛徹(哈哈,是痛徹!?。。┑牧私?,文章主要涵蓋如下內(nèi)容:

          • V8的內(nèi)存限制與解決辦法
          • 新生代內(nèi)存對(duì)象的Scavenge算法
          • 基于可達(dá)性分析算法標(biāo)記存活對(duì)象的邏輯以及優(yōu)化手段
          • 新生代內(nèi)存對(duì)象的晉升條件、
          • Scavenge算法的深度/廣度優(yōu)先區(qū)別
          • 跨代內(nèi)存的的寫(xiě)屏障
          • 老生代內(nèi)存對(duì)象的標(biāo)記清除/整理算法
          • GCSTW原因及優(yōu)化策略

          V8的內(nèi)存限制與解決辦法

          V8最初為瀏覽器設(shè)計(jì),遇到大內(nèi)存使用的場(chǎng)景較少,在設(shè)計(jì)上默認(rèn)對(duì)內(nèi)存使用存在限制,只允許使用部分內(nèi)存,64位系統(tǒng)可允許使用內(nèi)存約1.4g,32位系統(tǒng)約0.7g。如下代碼所示,在Node中查看所依賴的V8引擎的內(nèi)存限制方法:

          process.memoryUsage();  // 返回內(nèi)存的使用量,單位字節(jié) {   rss: 22953984,   // 申請(qǐng)的總的堆內(nèi)存   heapTotal: 9682944,   // 已使用的堆內(nèi)存   heapUsed: 5290344,   external: 9388 }

          聊聊V8的內(nèi)存管理與垃圾回收算法

          V8限制內(nèi)存使用大小還有另一個(gè)重要原因,堆內(nèi)存過(guò)大時(shí)V8執(zhí)行垃圾回收的時(shí)間較久(1.5g50ms),做非增量式的垃圾回收要更久(1.5g1s)。在后續(xù)講解了V8的垃圾回收機(jī)制后相信大家更能感同身受。

          雖然V8引擎對(duì)內(nèi)存使用做了限制,但是同樣暴露修改內(nèi)存限制的方法,就是啟動(dòng)V8引擎時(shí)添加相關(guān)參數(shù),下面代碼演示在Node中修改依賴的V8引擎內(nèi)存限制:

          # 更改老生代的內(nèi)存限制,單位mb node --max-old-space-size=2048 index.js  # 更改新生代的內(nèi)存限制,單位mb node --max-semi-space-size=1024=64 index.js

          這里需要注意的是更改的新生代的內(nèi)存的語(yǔ)法已經(jīng)更改為上述的寫(xiě)法,且單位也由kb變成了mb,舊的寫(xiě)法是node --max-new-space-size,可以通過(guò)下面命令查詢當(dāng)前Node環(huán)境修改新生代內(nèi)存的語(yǔ)法:

          node --v8-options | grep max

          聊聊V8的內(nèi)存管理與垃圾回收算法

          V8垃圾回收策略

          在引擎的垃圾自動(dòng)回收機(jī)制的歷史演變中,人們發(fā)現(xiàn)是沒(méi)有一種通用的可以解決任何場(chǎng)景下垃圾回收的算法的。因此現(xiàn)代垃圾回收算法根據(jù)對(duì)象的存活時(shí)間將內(nèi)存垃圾進(jìn)行分代,分代垃圾回收算法就是對(duì)不同類(lèi)別的內(nèi)存垃圾實(shí)行不同的回收算法。

          V8將內(nèi)存分為新生代老生代兩種:

          • 新生代內(nèi)存中的對(duì)象存活時(shí)間較短
          • 老生代內(nèi)存中代對(duì)象存活時(shí)間較長(zhǎng)或是常駐內(nèi)存

          新生代內(nèi)存存放在新生代內(nèi)存空間(semispace)中,老生代內(nèi)存存放在老生代內(nèi)存空間中(oldspace),如下圖所示:

          聊聊V8的內(nèi)存管理與垃圾回收算法

          • 新生代內(nèi)存采用Scavenge算法
          • 老生代內(nèi)存采用Mark-SweepMark-Compact算法

          下面我們看看Scavenge的算法邏輯吧!

          Scavenge算法

          對(duì)于新生代內(nèi)存的內(nèi)存回收采用Scavenge算法,Scavenge的具體實(shí)現(xiàn)采用的是Cheney算法。Cheney算法是將新生代內(nèi)存空間一分為二,一個(gè)空間處于使用狀態(tài)(FromSpace),一個(gè)空間處于空閑狀態(tài)(稱(chēng)為ToSpace)。

          聊聊V8的內(nèi)存管理與垃圾回收算法

          在內(nèi)存開(kāi)始分配時(shí),首先在FromSpace中進(jìn)行分配,垃圾回收機(jī)制執(zhí)行時(shí)會(huì)檢查FromSpace中的存活對(duì)象,存活對(duì)象會(huì)被會(huì)被復(fù)制到ToSpace,非存活對(duì)象所占用的空間將被釋放,復(fù)制完成后FromSpaceToSpace的角色將翻轉(zhuǎn)。當(dāng)一個(gè)對(duì)象多次復(fù)制后依然處于存活狀態(tài),則認(rèn)為其是長(zhǎng)期存活對(duì)象,此時(shí)將發(fā)生晉升,然后該對(duì)象被移動(dòng)到老生代空間oldSpace中,采用新的算法進(jìn)行管理。

          聊聊V8的內(nèi)存管理與垃圾回收算法

          Scavenge算法其實(shí)就是在兩個(gè)空間內(nèi)來(lái)回復(fù)制存活對(duì)象,是典型的空間換時(shí)間做法,所以非常適合新生代內(nèi)存,因?yàn)閮H復(fù)制存活的對(duì)象且新生代內(nèi)存中存活對(duì)象是占少數(shù)的。但是有如下幾個(gè)重要問(wèn)題需要考慮:

          • 引用避免重復(fù)拷貝

          假設(shè)存在三個(gè)對(duì)象temp1、temp2、temp3,其中temp2、temp3都引用了temp1,js代碼示例如下:

          var temp2 = {   ref: temp1, }  var temp3 = {   ref: temp1, }  var temp1 = {}

          FromSpace中拷貝temp2ToSpace中時(shí),發(fā)現(xiàn)引用了temp1,便把temp1也拷貝到ToSpace,是一個(gè)遞歸的過(guò)程。但是在拷貝temp3時(shí)發(fā)現(xiàn)也引用了temp1,此時(shí)再把temp1拷貝過(guò)去則重復(fù)了。

          要避免重復(fù)拷貝,做法是拷貝時(shí)給對(duì)象添加一個(gè)標(biāo)記visited表示該節(jié)點(diǎn)已被訪問(wèn)過(guò),后續(xù)通過(guò)visited屬性判斷是否拷貝對(duì)象。

          • 拷貝后保持正確的引用關(guān)系

          還是上述引用關(guān)系,由于temp1不需要重復(fù)拷貝,temp3被拷貝到ToSpace之后不知道temp1對(duì)象在ToSpace中的內(nèi)存地址。

          做法是temp1被拷貝過(guò)去后該對(duì)象節(jié)點(diǎn)上會(huì)生成新的field屬性指向新的內(nèi)存空間地址,同時(shí)更新到舊內(nèi)存對(duì)象的forwarding屬性上,因此temp3就可以通過(guò)舊temp1forwarding屬性找到在ToSpace中的引用地址了。

          內(nèi)存對(duì)象同時(shí)存在于新生代和老生代之后,也帶來(lái)了問(wèn)題:

          • 內(nèi)存對(duì)象跨代(跨空間)后如何標(biāo)記
          const temp1 = {}  const temp2 = {   ref: temp1, }

          比如上述代碼中的兩個(gè)對(duì)象temp1temp2都存在于新生代,其中temp2引用了temp1。假設(shè)在經(jīng)過(guò)GC之后temp2晉升到了老生代,那么在下次GC的標(biāo)記階段,如何判斷temp1是否是存活對(duì)象呢?

          在基于可達(dá)性分析算法中要知道temp1是否存活,就必須要知道是否有根對(duì)象引用引用了temp1對(duì)象。如此的話,年輕代的GC就要遍歷所有的老生代對(duì)象判斷是否有根引用對(duì)象引用了temp1對(duì)象,如此的話分代算法就沒(méi)有意義了。

          解決版本就是維護(hù)一個(gè)記錄所有的跨代引用的記錄集,它是寫(xiě)緩沖區(qū)的一個(gè)列表。只要有老生代中的內(nèi)存對(duì)象指向了新生代內(nèi)存對(duì)象時(shí),就將老生代中該對(duì)象的內(nèi)存引用記錄到記錄集中。由于這種情況一般發(fā)生在對(duì)象寫(xiě)的操作,顧稱(chēng)此為寫(xiě)屏障,還一種可能的情況就是發(fā)生在晉升時(shí)。記錄集的維護(hù)只要關(guān)心對(duì)象的寫(xiě)操作和晉升操作即可。此是又帶來(lái)了另一個(gè)問(wèn)題:

          • 每次寫(xiě)操作時(shí)維護(hù)記錄集的額外開(kāi)銷(xiāo)

          優(yōu)化的手段是在一些Crankshaft操作中是不需要寫(xiě)屏障的,還有就是棧上內(nèi)存對(duì)象的寫(xiě)操作是不需要寫(xiě)屏障的。還有一些,

          贊(0)
          分享到: 更多 (0)
          網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)