V8 內存管理

一、內存查看

console.log(
    '\n node分配到的總內存 rss: ' + mem.rss,
    '\n 堆內存 heapTotal: ' + mem.heapTotal,
    '\n 已使用的內存 heapUsed: ' + mem.heapUsed,
    
    '\n node中額外申請到的c++內存: ' + mem.external,

    '\n 操作系統總內存: ' + os.totalmem(),
    '\n 操作系統空閒內存: ' + os.freemem()
)

注意單位都是字節。

二、內存分配

V8 會將系統分配給它的內存分爲新生代內存和老生代內存,新生代內存用於存放存活時間較短的對象,所以這裏的內存會被 gc 頻繁的回收。而老生代內存用於存放存活時間久的對象,二者的垃圾回收策略也各不相同。

三、新生代

新生代將內存一分爲二,一塊處於使用中(From),一塊處於閒置中(To)。gc 的過程大致如下

  1. 新對象首先進入 From
  2. gc 時,將 From 中存活的對象複製到 To 中,並給這些變量加個標記,表示它已經經歷過一次 gc 了,而且存活下來了(現在 To 空間中的變量都是需要保留下來的)
  3. 清除 From 空間
  4. 將 From 和 To 空間互換,那麼現在 From 空間全都是存活的變量(因爲我們要保證 From 空間始終代表使用中,To 空間代表閒置)
  5. 到這裏就完成了一次 gc

新生代對象晉升

當新生代對象滿足一定條件後,會晉升到老生代空間中。條件如下

  • gc 時,如果發現一個對象之前經歷過 gc 並且存活下來了,就晉升到老生代
  • gc 時,將 From 中的存活變量複製到 To 時,如果 To 空間已經超過 25%,那麼將改對象放入老生代空間中,而不是 To 空間

四、老生代

老生代中內存是連續的,不像新生代中被分成了兩塊。老生代中的垃圾回收有兩種方式:標記清除(Mark Sweep)和標記合併(Mark Concat)

Mark Sweep

標記清除分爲兩個階段:標記階段、清除階段,標記階段將非存活的對象標記出來,在清除階段將這些對象清除,釋放內存。

標記清除法會有個問題,死亡對象被清除之後會留下很多內存碎片,這時候有大對象要存儲的時候會沒地方存,這時候需要對內存進行整理

Mark Concat

標記合併分爲兩個階段:標記階段、合併階段,標記階段將存活都標記出來,然後將這些變量都移動到內存的一端,最後將邊界外的內存都清理掉

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章