李彥廣趙科研
(1 商洛學(xué)院數(shù)學(xué)與計(jì)算機(jī)應(yīng)用學(xué)院,陜西 商洛 726000)(2 商洛學(xué)院經(jīng)濟(jì)與管理學(xué)院,陜西 商洛 726000)
淺析Node.js中的內(nèi)存控制機(jī)制
李彥廣1趙科研2
(1 商洛學(xué)院數(shù)學(xué)與計(jì)算機(jī)應(yīng)用學(xué)院,陜西 商洛 726000)
(2 商洛學(xué)院經(jīng)濟(jì)與管理學(xué)院,陜西 商洛 726000)
在V8平臺上構(gòu)建Node.js,用戶在使用過程中要通過JavaScricot進(jìn)行調(diào)用的情況下,對Node.js在內(nèi)存的管理進(jìn)行分析介紹,在V8平臺上的對內(nèi)存的分配和回收機(jī)制進(jìn)行了分析,提出了在使用單個(gè)Node.js進(jìn)程的下,如何合理高效使用計(jì)算機(jī)的內(nèi)存資源。經(jīng)過測試仿真,方法是高效的。
Node.js V8 JavaScricot內(nèi)存分配 回收策略
在Node.js中通過JavaScricpt使用內(nèi)存通常會受到限制(64位系統(tǒng)下約為1.4 GB,32位系統(tǒng)下約為0.7 GB)[1]。這將導(dǎo)致Node.js無法直接操作大內(nèi)存對象,例如即使物理內(nèi)存有32 GB,也無法將一個(gè)2 GB的文件讀入內(nèi)存中進(jìn)行字符串分析處理。這樣在單個(gè)Node.js進(jìn)程的情況下,計(jì)算機(jī)的內(nèi)存資源無法得到充分利用。盡管服務(wù)器端操作大內(nèi)存也不是常見場景,但有了限制后,人們的行為就如同帶著鐐銬跳舞,如果在實(shí)際的應(yīng)用中不小心觸碰到這個(gè)界限,會造成程序退出[2]。要知曉V8為何限制了內(nèi)存的用量,則需要回歸到V8在內(nèi)存使用上的策略。知曉其原理后,才能避免問題,并更好地進(jìn)行內(nèi)容管理。
在V8中,所有JavaScript對象都是通過堆來進(jìn)行分配的。Node.js提供了V8中內(nèi)存使用量的查看方式,執(zhí)行下面的代碼,將得到輸出的內(nèi)存信息:
上述代碼中,在memoryUsage()方法返回的3個(gè)屬性,heapTotal和heapUsed是V8的堆內(nèi)存使用情況,前者是以申請到的堆內(nèi)存,后者是當(dāng)前使用的量,V8的堆示意圖如圖1所示。
圖1 V8的堆示意圖
當(dāng)在代碼中聲明變量并賦值時(shí),所使用對象的內(nèi)存就分配在堆中。如果已申請的堆空閑內(nèi)存不夠分配新的對象,將繼續(xù)申請對內(nèi)存,直到堆的大小超過V8的限制為止[3]。
至于V8為何要限制堆的大小,表層原因是V8最初為瀏覽器而設(shè)計(jì),不太可能用到大量內(nèi)存的場景,深層原因是V8的垃圾回收機(jī)制的限制,按官方的說法,以1.5 GB的堆內(nèi)存為例,V8做一次小的垃圾回收需要50 ms以上,做一次非增量式的垃圾回收甚至需要一秒以上。這是垃圾回收中引起JavaScript線程暫停執(zhí)行的時(shí)間,在這樣的時(shí)間花銷下,應(yīng)用的性能和相應(yīng)能力會直線下降。這樣的情況不僅后端服務(wù)無法接受,前端瀏覽器也無法接受。因此,在當(dāng)時(shí)的考慮下,直接限制內(nèi)存是一個(gè)合理的解釋。
當(dāng)然,這個(gè)限制也不是不能打開,V8依然提供了選項(xiàng)讓人們使用更多的內(nèi)存。Node.js在啟動時(shí)可以傳遞--max-old-space-size或--max-new-space-size來調(diào)整內(nèi)存限制的大小,示例如下:
node--max-old-space-size=1700 test.js
node--max-new-space-size=1024 test.js
上述參數(shù)在V8初始化時(shí)生效并且不能再動態(tài)改變。如果遇到Node.js無法分配足夠內(nèi)存給JavaScript對象的情況,可以用此辦法來放寬V8默認(rèn)的內(nèi)存限制,避免在執(zhí)行過程中多用了一些內(nèi)存就輕易崩潰[4]。
V8的垃圾回收策略主要基于分代式垃圾回收機(jī)制。在自動垃圾回收的演變過程中,人們發(fā)現(xiàn)沒有一種垃圾回收算法能勝任所有的場景。因?yàn)樵趯?shí)際應(yīng)用中,對象的生存周期長短不一,不同算法只能針對特殊情況具有最好的效果。為此統(tǒng)計(jì)學(xué)在垃圾回收算法的發(fā)展中產(chǎn)生了較大的作用,現(xiàn)代垃圾回收算法中按對象的存活時(shí)間將內(nèi)存的垃圾回收進(jìn)行不同的分代,然后分別對不同的分代的內(nèi)存施以更高效的算法[5]。
3.1 V8的內(nèi)存分代
在V8中,主要分為新生代和老生代。新生代中的對象為存活時(shí)間較短的對象,老生代中的對象為存活時(shí)間較長或常駐內(nèi)存的對象,V8的分代示意圖如圖2所示。
圖2 V8的分代示意圖
V8的整體大小就是新生代所用內(nèi)存空間加上老生代的內(nèi)存空間,前面提及的--mac-old-space-size命令行參數(shù)可用于設(shè)置老生代內(nèi)存空間的最大值,--max-new-space-size命令行參數(shù)則用于設(shè)置新生代內(nèi)存空間的大小。比較遺憾的是,這2個(gè)最大值需要在啟動時(shí)指定。這意味著V8使用的內(nèi)存無法依據(jù)使用情況自動擴(kuò)充[6]。當(dāng)內(nèi)存分配過程中超過極限值時(shí),就會引起出錯(cuò)。
前面提到,在默認(rèn)值下如果一直分配內(nèi)存,在64位系統(tǒng)和32位系統(tǒng)下會分別只能使用約1.4 GB和0.7 GB。這個(gè)限制可從V8的源碼中找到。在下面的代碼中,Page::kPageSize的值為1 MB。可以看到,老生代的設(shè)置在32位系統(tǒng)下為1 400 MB,在32位系統(tǒng)下為700 MB:
對于新生代,它由2個(gè)reserved-semispace-size-構(gòu)成,后面將描述其原因。按機(jī)器位數(shù)不同,reserved-semispace-size-在64位系統(tǒng)和32位系統(tǒng)上分別為16 MB和8 MB,所以新生代內(nèi)存的最大值在64位系統(tǒng)和32位系統(tǒng)上分別為32 MB和16 MB。
V8堆內(nèi)存的最大保留空間公式為4?reserved-semispacesize-+max-old-space-size-。因此,默認(rèn)情況下,V8堆內(nèi)存的最大值在64位系統(tǒng)上為146 MB,32位系統(tǒng)上則為732 MB。這個(gè)數(shù)值可解釋為何在64位系統(tǒng)下只能使用約1.4 GB內(nèi)存和在32位系統(tǒng)下只能使用0.7 GB。
3.2 Scavenge算法
在分代的基礎(chǔ)上,新生代的對象主要通過Scavenge算法進(jìn)行垃圾回收。在Scavenge的具體實(shí)現(xiàn)中,主要采用了Cheney算法,該算法由C.J.Cheney于1970年首次發(fā)表在ACM論文上。
Cheney算法是一種采用復(fù)制的方式實(shí)現(xiàn)的垃圾回收算法,它將堆內(nèi)存一分為二,每一部分空間稱為semispace。在這2 個(gè)semispace空間中,只有一個(gè)處于使用中,另一個(gè)處于閑置。處于使用狀態(tài)的semispace空間稱為From空間,處于閑置的空間稱為To空間。在分配對象時(shí),先是針對From空間中的存活對象,這些存活對象將被復(fù)制到To空間中,而非存活對象占用的空間的角色發(fā)生對換。簡而言之,在垃圾回收的過程中,就是通過將存活對象在2個(gè)semispace空間之間進(jìn)行復(fù)制。Scavenge的缺點(diǎn)是只能使用堆內(nèi)存中的一半,這是由劃分空間和復(fù)制機(jī)制所決定的。但Scavenge由于只復(fù)制存活對象,并且對于生命周期短的場景存活對象只占少部分,所以它在時(shí)間效率上有優(yōu)異的表現(xiàn)。
由于Scavenge是典型的犧牲空間換取時(shí)間的算法,所以無法大規(guī)模地應(yīng)用到所有的垃圾回收中。但可以發(fā)現(xiàn),Scavenge非常適合應(yīng)用在新生代中,因?yàn)樾律袑ο蟮纳芷谳^短,恰恰是和這個(gè)算法,故而V8的對內(nèi)存示意圖應(yīng)當(dāng)如圖3所示。
圖3 V8的堆內(nèi)存示意圖
實(shí)際使用的對內(nèi)存是新生代中的2個(gè)semispace空間大小和老生代所用內(nèi)存大小之和。當(dāng)一個(gè)對象經(jīng)過多次復(fù)制依然存活時(shí),它將被認(rèn)為是生命周期較長的對象。這種較長生命周期對象隨后會被移動到老生代中,采用新的算法進(jìn)行管理[6]。對象從新生代移動到老生代中的過程稱為晉升。
在單純的Scavenge過程中,F(xiàn)rom空間中的存活對象會復(fù)制到To中,然后對From空間和To空間進(jìn)行角色對換(又稱翻轉(zhuǎn))。但在分代式垃圾回收的前提下,F(xiàn)rom空間中的存活對象在復(fù)制前需要進(jìn)行檢查。在一定條件下,需將存活周期長的對象移動到老生代中,也就是完成對象晉升。對象晉升的條件主要有2個(gè):一個(gè)是對象是否經(jīng)歷過Scavenge回收,一個(gè)是To空間的內(nèi)存占用比超過限制。
3.3 查看垃圾回收日志
查看垃圾回收日志的方式主要是在啟動時(shí)添加--trace-gc參數(shù),在進(jìn)行垃圾回收時(shí)將會從標(biāo)準(zhǔn)輸出中打印垃圾回收的日志信息,執(zhí)行結(jié)束后,將會在gc-log文件中得到所有垃圾回收信息。通過分析垃圾回收日志,可以了解垃圾回收的運(yùn)行狀況,找出垃圾回收的哪些階段比較耗時(shí),觸發(fā)的根本原因是什么。
從V8的自動垃圾回收機(jī)制的實(shí)際角度可以看到,V8對內(nèi)存使用進(jìn)行限制的緣由,新生代設(shè)計(jì)為一個(gè)較小的內(nèi)存空間是合理的,而老生代空間過大對于垃圾回收并無特別意義。V8對內(nèi)存限制的設(shè)置對于Chrome瀏覽器這種每個(gè)選項(xiàng)卡頁面使用一個(gè)V8實(shí)例而言,內(nèi)存的使用是綽綽有余了。對Node.js編寫的服務(wù)器端來說,限制也并不影響正常場景下的服務(wù)。但對于V8的垃圾回收特點(diǎn)和JavaScricot在單線程上的執(zhí)行情況,垃圾回收是影響性能的因素之一。想要注意垃圾回收盡量少地進(jìn)行,尤其是全堆垃圾回收。以Web服務(wù)器中的會話實(shí)現(xiàn)為例,一般是通過內(nèi)存來存儲,但在訪問量大的時(shí)候會導(dǎo)致老生代中的存活對象驟增,不僅造成清理和整理過程費(fèi)時(shí),還會造成內(nèi)存緊張,甚至溢出。
[1]樸 靈.深入淺出Node.js[M].北京:人民郵電出版社,2013,12:22-32.
[2]李 梅.淺談Node.js異步編程中回調(diào)和異步調(diào)用的區(qū)別[J].通信世界,2015(6):203.
[3]李彥廣.基于Spark+MLlib分布式學(xué)習(xí)算法的研究[J].商洛學(xué)院學(xué)報(bào),2015,29(2):16-19.
[4]萬里晴,楊 浩.探究基于V8引擎的Node.js在各應(yīng)用領(lǐng)域的發(fā)展[J].通信世界,2015(7):97.
[5]樸 靈.深入淺出Node.js[M].北京:人民郵電出版社,2013,12:45-55.
[6]李彥廣.基于服務(wù)特性的CDN帶寬動態(tài)分配策略[J].西安工業(yè)大學(xué)學(xué)報(bào),2015(5):365-368.
A Brief Analysis of Memory Control Mechanisms for Node.js
LI Yan-guang1,ZHAO Ke-yan2
(1.College of Mathematics and Computer Application,Shangluo University,Shangluo Shaanxi 726000,China)
(2.Faculty of Economics and Management,Shangluo University,Shangluo Shaanxi 726000,China)
Users make a call by JavaScricot in building the Node.js in V8 platform.This paper analyzes and introduces the application of Node.js in memory management.The memory distribution and recovery mechanism are analyzed in V8 platform.This paper puts forward the method of using memory resource of computer reasonably and effectively in a single Node.js.The test and simulation results show that this method has high availability.
Node.js;V8;JavScricot;memory allocation;recovery strategy
TP393.4
A
1008-1739(2015)24-63-3
定稿日期:2015-11-26
陜西省教育廳科研專項(xiàng):商洛市農(nóng)村中小學(xué)信息技術(shù)教師專業(yè)素養(yǎng)評價(jià)及專業(yè)發(fā)展途徑研究(2013jk1160);陜西省教育廳科研專項(xiàng):天梯(2286)。