摘要:基本概念垃圾回收機制。對兩個不同生代的不同垃圾回收策略構成了整個的垃圾回收機制。此種方式會導致下一次內存中產生大量碎片,即內存空間不連續,導致內存分配時面對大對象可能會無法滿足,提前出發下一次的垃圾回收機制。
: 聊一聊垃圾回收機制吧。基本概念
: 恩,垃圾回收是自動的。
GC(Garbage collection)垃圾回收機制。目的是解釋器去判別需要回收的內容,當解釋器認為一個占著房子的人已經沒有存在的意義了,就自動收回房子重新對外出租(available)。JS和PY都選擇不相信程序員,選擇自己操控內存問題。
Node的對象都是分配在堆內存上,V8主要把內存分為 new-space 和 old-space ,64位系統對應的大小約為 32MB 和 1400MB(32位系統對應折半)。二者共同構成Node的總內存(約為1.4G)。
新生代空間的對象生存周期比較短,容量也比較小,老生代的對象都是“強硬派”,生命力旺盛,容量比較大。Node 不是 HipHop 為啥非要把內存分這個 “new-school”,“old-school” ?,就是因為在實際的情況中,各種垃圾回收策略并不能滿足解決不同的對象聲明周期長短不一的問題,而只是針對某一種特定情況非常有用,所以基于分代策略能夠根據對象的生命周期不同,采用最適合的算法策略進行高效垃圾回收。
Node對兩個不同生代的不同垃圾回收策略構成了整個Node的垃圾回收機制。下面就來詳細說明這兩個不同的生代究竟是怎么處理的辣雞的。
new-space 與 Scavenge算法回顧一下 new-space 的特點:對象的生存周期普遍都比較短。這意味著,“頑固派”對象比較少
Scavenge 策略把 new-space 一分為兩個 “simispace"(半空間),一個叫 處于使用狀態的 From 空間 一個叫閑置的 TO 空間。整個回收的過程就是如下圖:
引用計數與閉包那么在新生代中如何讓GC知道某一個對象已經沒有價值即該對象的生命周期已經結束了呢?
引用計數:所謂引用計數就是跟蹤并記錄每一個值被引用的次數,當我們生命了一個變量并且將一個引用類型賦值給該變量,那么該引用對象的引用計數加一,如果同一個變量又賦值給了另外一個變量,那么計數再一次增加1。那么相反的是如果某一個有引用類型值得變量又被賦了另外一個值,那么原先的引用類型的計數就相應的減一,或者當在一個函數執行完畢之后,該函數在執行時所創建的作用域將銷毀,與此同時在該函數作用域中聲明的局部變量所對應的內存空間的引用計數將隨之減一,不出現閉包的情況下,下一次的垃圾回收機制在被觸發的時候,作用域中的變量所對應的空間就會結束聲明周期。像下面的代碼那樣:
function callOnce(){ let local = {} let foo = {} let bar = {a:{},b:{}} }
那么所謂閉包,一個在面試中都快被問爛了的概念:),其實說白了就是運用函數可以作為參數或者返回值使得一個外部作用域想要訪問內部作用域中的私有變量的一種方式
function foo(){ let local = {a:"ray"} return function(){ return local } } let bar = foo()
上述代碼就形成了一個閉包,使得一旦有了變量引用了foo函數的返回值函數,就使得該返回值函數得不到釋放,也使得foo函數的作用域得不到釋放,即內存也不會釋放,除非不再有引用,才會逐步釋放。
old-space 與 標記-清除/標記-整理分代之中除了 new-space 之外即是 old-space 了 ,分代的目的是為了針對不同的對象生命周期運用不同的回收算法。
滿足條件晉升到老生代的的對象都有著比較頑強的生機,意味著在老生代中,存活的對象占有者很大的比重,使用新生代基于復制的策略會有著比較差的效率,此外,新生代中一分為二的空間策略面對著存活對象較多的情況也比較不合適。所以在老生代中V8采用了標記-清除與標記-整理這這兩種方式結合的策略。
標記清除分為標記和清除兩個步驟,先在老生代中遍歷所有的對象,把那些在遍歷過程中還活著的對象都加上一個標記,在下一步的時候那些沒有被標記的對象就會自然的被回收了。示意圖如下:
黑色的即為沒有被標記已經死了對象,下一次就會被回收內存空間。
此種方式會導致下一次內存中產生大量碎片,即內存空間不連續,導致內存分配時面對大對象可能會無法滿足,提前出發下一次的垃圾回收機制。所以便又有了一種標記-整理的方式。
對比標記-清除,他多了異步整理的過程,即把標記為存活的兌現統統整理到內存的一端,完成整理之后直接清除掉另一端連續的死亡對象空間,如下:
最后,由于標記-整理這種方式設計大量移動對象操作,導致速度非常慢,多以 V8 主要使用標記-清除的方式,當老生代空間中不足以為新生代晉升過來的頑固派們分配空間的時候,才使用標記-整理
V8的優化由于在進行垃圾回收的時候會導致應用邏輯陷入全停頓的狀態,在進行老生代的回收時,V8引入了 增量式標記,增量式整理,延遲清理等策略,中心思想就是為了能讓一次垃圾回收過程不那么占用太長的應用程序停頓時間,而提出類似于時間片輪轉一樣的策略,讓整個過程“雨露均沾”,GC弄一會,應用程序執行一會。
堆內內存與堆外內存使用process.memoryUsage()可以查看node進程的內存使用情況。單位是字節
{ rss: 22233088, heapTotal: 7708672, heapUsed: 5095384, external: 28898 }
其中 rss 就是 node 進程的常駐內存。V8對內存有限制,但是不同于瀏覽器,Node在服務端難免會操作大文件流,所以有了一種跳脫 V8 的內存限制方式就是使用 buffer 進行堆外內存分配。如下代碼:
let showMem = () => { let mem = process.memoryUsage() //process.memoryUsage()值得單位都是字節,轉化為兆 let format = (byte) => { return (byte/1024/1024).toFixed(2)+"MB" } console.log(`rss:${format(mem.rss)} heapTotal:${format(mem.heapTotal)} heapUsed:${format(mem.heapUsed)} external:${format(mem.external)}`); console.log("------------------------------------"); } let useMem = () => { let size = 20*1024*1024 let arr = new Array(size) for (let index = 0; index < size; index++) { arr[index] = 0 } return arr } let useMemBuffer = () => { let size = 20*1024*1024 let buf = new Buffer(size) for (let index = 0; index < size; index++) { buf[index] = 0 } return buf } let total = [] for (let index = 0; index < 100; index++) { showMem() total.push(useMemBuffer()) } showMem()
下面為分別調用 useMem()和useMemBuffer() 使用數組是通過V8分配堆內存,使用 Buffer 是不使用V8分配堆外內存,分別打?。?/p>
上圖一表示堆內內存在一定循環次數之后達到溢出邊緣,
圖二可見,external和rss在不斷增大但是其值早就突破了V8的內存上限。是因為堆外內存并不是V8進行內存分配的。
下一篇所要討論的緩存算法中,緩存就是一個有可能造成內存泄漏的場景。
參考:
《深入淺出NodeJS》-- 樸靈
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93505.html
摘要:垃圾回收內存管理實踐先通過一個來看看在中進行垃圾回收的過程是怎樣的內存泄漏識別在環境里提供了方法用來查看當前進程內存使用情況,單位為字節中保存的進程占用的內存部分,包括代碼本身棧堆。 showImg(https://segmentfault.com/img/remote/1460000019894672?w=640&h=426);作者 | 五月君Node.js 技術棧 | https:...
摘要:的內存限制和垃圾回收機制內存限制內存限制一般的后端語言開發中,在基本的內存使用是沒有限制的。的內存分代目前沒有一種垃圾自動回收算法適用于所有場景,所以的內部采用的其實是兩種垃圾回收算法。 前言 從前端思維轉變到后端, 有一個很重要的點就是內存管理。以前寫前端因為只是在瀏覽器上運行, 所以對于內存管理一般不怎么需要上心, 但是在服務器端, 則需要斤斤計較內存。 V8的內存限制和垃圾回收機...
摘要:一前言的垃圾回收機制使用垃圾回收機制來自動管理內存。垃圾回收器只會針對新生代內存區老生代指針區以及老生代數據區進行垃圾回收。分別對新生代和老生代使用不同的垃圾回收算法來提升垃圾回收的效率。 V8 實現了準確式 GC,GC 算法采用了分代式垃圾回收機制。因此,V8 將內存(堆)分為新生代和老生代兩部分。 一、前言 V8的垃圾回收機制:JavaScript使用垃圾回收機制來自動管理內存。垃...
摘要:新生代的對象為存活時間較短的對象,老生代中的對象為存活時間較長或常駐內存的對象。分別對新生代和老生代使用不同的垃圾回收算法來提升垃圾回收的效率。如果指向老生代我們就不必考慮它了。 這篇文章的所有內容均來自 樸靈的《深入淺出Node.js》及A tour of V8:Garbage Collection,后者還有中文翻譯版V8 之旅: 垃圾回收器,我在這里只是做了個記錄和結合 垃圾回收...
摘要:關鍵是釋放內存這一步,各種語言都有自己的垃圾回收簡稱機制。用的是這種,在字末位進行標識,為指針。對于而言,最初的垃圾回收機制,是基于引用計次來做的。老生代的垃圾回收,分兩個階段標記清理有和這兩種方式。 不管是高級語言,還是低級語言。內存的管理都是: 分配內存 使用內存(讀或寫) 釋放內存 前兩步,大家都沒有太大異議。關鍵是釋放內存這一步,各種語言都有自己的垃圾回收(garbage ...
閱讀 2958·2021-11-08 13:20
閱讀 1031·2021-09-22 15:20
閱讀 660·2019-08-30 15:53
閱讀 1964·2019-08-30 15:43
閱讀 1278·2019-08-29 17:21
閱讀 540·2019-08-29 12:15
閱讀 2375·2019-08-28 17:51
閱讀 3142·2019-08-26 13:26