這篇文章主要介紹 V8 的內(nèi)存管理和垃圾回收知識(shí)。(總結(jié)來(lái)自網(wǎng)易課堂)
V8引擎內(nèi)存回收機(jī)制
為什么我們要關(guān)注內(nèi)存呢?
- 防止頁(yè)面占用內(nèi)存過(guò)大,引起客戶端卡頓,甚至無(wú)響應(yīng)
- Node使用的也是V8,內(nèi)存對(duì)于后端服務(wù)的性能至關(guān)重要。因?yàn)榉?wù)的持久性,后端很容易造成內(nèi)存溢出。
V8內(nèi)存結(jié)構(gòu)
在 64 位的機(jī)器上,默認(rèn)最大操作的對(duì)象大小約為 1.4G,在 32 位的機(jī)器上,默認(rèn)最大操作的對(duì)象大小約為 0.7G。
V8 將內(nèi)存分為兩類(lèi):新生代內(nèi)存空間和老生代內(nèi)存空間,新生代內(nèi)存空間主要用來(lái)存放存活時(shí)間較短的對(duì)象,老生代內(nèi)存空間主要用來(lái)存放存活時(shí)間較長(zhǎng)的對(duì)象。 64位新生代的空間為64MB,老生代為1400MB。32位下新生代的空間為16MB,老生代為700MB。

V8內(nèi)存結(jié)構(gòu)
新生代的變量,是一個(gè)復(fù)制的過(guò)程,假設(shè)有a,b,c三個(gè)變量,如果c不使用了,就會(huì)把From中的變量a、b復(fù)制到to里面,然后把from里面全部清空。下一次,如果b死掉了,再把a(bǔ)從to復(fù)制到from,然后把to清空。
老生代的回收,是標(biāo)記死掉的變量,在垃圾回收的時(shí)候,把死掉的變量全部清除掉,然后進(jìn)行磁盤(pán)碎片處理。
標(biāo)記清除法主要分三步:
- 將所有的指針(變量名)和分配出去的內(nèi)存打上標(biāo)記。
- 從棧區(qū)開(kāi)始查找所有可用的變量名,清除這些變量名的標(biāo)記,并且清除它們指向的內(nèi)存的標(biāo)記。
-
標(biāo)記清除結(jié)束后,回收所有仍然帶有標(biāo)記的內(nèi)存(說(shuō)明沒(méi)有一個(gè)有效的變量名指向這塊內(nèi)存)。
image.png
新生代如何晉升到老生代。
必須要滿足2個(gè)條件:
1.變量必須要經(jīng)歷過(guò)回收(也就是復(fù)制)
2.To空間已經(jīng)占用了百分之25。
利用node來(lái)查看內(nèi)存使用情況
通過(guò)process.memoryUsage()

查看V8引擎內(nèi)存
其中res是V8總內(nèi)存,heapTotal:是堆總內(nèi)存,heapUsed:是堆使用內(nèi)存,external:是額外C++內(nèi)存。
我們重點(diǎn)關(guān)注一下heapUsed
V8引擎是如何處理變量
內(nèi)存主要是用來(lái)儲(chǔ)存變量等數(shù)據(jù),局部變量當(dāng)程序執(zhí)行結(jié)束,且沒(méi)有引用的時(shí)候,就會(huì)隨之消失。而全局變量會(huì)始終存貨到程序運(yùn)行結(jié)束。
function getme(){
var mem = process.memoryUsage();
var format = function(bytes){
return (bytes/1024/1024).toFixed(2)+'MB';
};
console.log('Process: heapTotal '+format(mem.heapTotal) +
' heapUsed ' + format(mem.heapUsed) +
' rss ' + format(mem.res)
);
}
上面這個(gè)函數(shù)可以在node環(huán)境中輸出V8內(nèi)存的使用情況。
如何注意內(nèi)存的使用
優(yōu)化內(nèi)存的技巧
- 盡量不要定義全局變量
- 全局變量記得銷(xiāo)毀掉
有兩種方式,第一種是delete,但是delete在嚴(yán)格模式下是禁止的。
第二種,是將變量 = undefined 或者 變量 = null - 用匿名自執(zhí)行函數(shù)變?nèi)譃榫植?/li>
- 盡量避免閉包
防止內(nèi)存泄漏
- 濫用緩存
- 大內(nèi)存量操作
