为什么以下的代码没有释放掉内存?
发布于 9 年前 作者 wf744 7615 次浏览 最后一次编辑是 8 年前 来自 问答
//显示内存使用情况
var showMem = function(){
	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.rss));
	console.log("-------------------------------------------------------");
}

//申请内存
var useMem = function(){
	var size = 20*1024*1024;
	var arr = new Array(size);
	for( var i=0; i<size; i++){
		arr[i] = 0;
	}
	return arr;
}

var total = [];

for( var j = 0; j<5; j++){
	showMem();
	total.push(useMem());
}

//清理内存
for( var i=0; i<total.length; i++){
	total[i] = undefined;
}
total = undefined;
console.log("清理内存完毕");
setInterval(showMem, 5000);
15 回复

垃圾回收不及时,v8引擎的垃圾回收机制问题

@jerrywu55 我放在哪里放了20分钟,都不回收内存,真不知道是什么回事

@wf744 并不是放了一段时间v8就回收内存的,是随着内存的使用,而扫描内存的使用情况,然后然后在适当的时机去回收。具体可以看一下《深入浅出Node.JS》,里面有很详细的介绍

@crystaldust 这个例子就是《深入浅出Node.JS》里面的,只不过书里面只说了内存是如何分配的,却没有说到上面这个例子的内存回收; 因为我的服务器遇到了一个问题就是RSS内存一直往上涨,一直涨到几G,但是堆内存是正常的,能正常回收和正常分配,所以想从最简单的例子入手,却发现最简单的例子(上面的代码),也不会自动回收

–trace_gc 加上看看就知道了

内存一直没变化,是不做gc的,稍微变动下内存,立马释放了。。。

@fantasyni 如何变动?我增加了以下这样的代码18秒后继续申请,内存继续上涨,没有任何降低的趋势

setTimeout(function(){var aaa = new Array(20000); console.log(“变动内存”);}, 18000);

改成死循环,然后看会不会OOM。

console.log(“清理内存完毕”); setInterval(showMem, 5000);

改成

setInterval(function() {
  useMem();
  showMem();
}, 5000);

node -trace_gc foo.js

@dayuoba

这方法有用,能回收了,但是为什么不动就不回收呢,这个就很奇怪

因该 =null 吧

@wf744 应该是因为做完用undefined赋值这一步之后,因为并没有继续使用内存,所以v8并没有扫描内存的使用情况,也就无法触发回收;或者有扫描,但是v8认为还不至于触发回收。

用10楼仁兄的方法,因为有继续分配内存,所以v8会扫描内存并在适当的时机触发回收。 我打印的结果是这样的,其中status是触发了回收时打印的标记:

process: heapTotal 6.32 MB heapUsed 3.46 MB rss 13.79 MB
-------------------------------------------------------
process: heapTotal 169.32 MB heapUsed 163.78 MB rss 174.73 MB
-------------------------------------------------------
process: heapTotal 330.32 MB heapUsed 323.32 MB rss 335.45 MB
-------------------------------------------------------
process: heapTotal 490.34 MB heapUsed 483.34 MB rss 495.46 MB
-------------------------------------------------------
process: heapTotal 650.36 MB heapUsed 643.34 MB rss 655.47 MB
-------------------------------------------------------
清理内存完毕
stats!

process: heapTotal 970.40 MB heapUsed 963.41 MB rss 975.73 MB
-------------------------------------------------------
process: heapTotal 1130.42 MB heapUsed 1123.46 MB rss 1135.73 MB
-------------------------------------------------------
process: heapTotal 331.31 MB heapUsed 323.36 MB rss 335.85 MB
-------------------------------------------------------
stats!
process: heapTotal 491.33 MB heapUsed 483.39 MB rss 495.85 MB
-------------------------------------------------------

@wf744 你的代码并没有问题,你的理解也没有问题。走到最下面 你对total[i]=undefined是会进行垃圾回收的。

为什么这里没有显示出来,我的解释如下:

  1. 因为你在for(var j=0;j<5;j++){} 这里进行申请堆内存,你5次循环后申请的堆内存大约在810M左右。并没有触发到V8的内存上限(1400MB左右),常驻在老生代,因此不触发Full GC; ====> 你可以通过把循环改大,比如到50次,这个时候会一直申请堆内存,直到分配不了而出错 gc1.jpg

2 当你只有5次申请内存后,进入下面的清理内存行为,即你的第2个循环。for(var i=0;i<total.length;i++){},是进行老生代的mark行为,也就是标记死亡对象的行为,要老生代进行sweep行为或者compact行为,才能真正意义的释放老生代的堆内存空间。而你要触发这个行为,就是下一次申请老生代堆内存的行为;类似再一次执行你的useMem()方法。 ====> 你可以通过在第一个循环(第1个循环,不是第2个循环),修改为这样的代码 gc2.jpg 然后你执行node --trace_gc index.js 会得到下面的结果,就验证了你的total[i]=undefined是有释放内存的效果的、 gc3.jpg 最后50次执行完,可以看到如下效果 gc4.jpg 这个时候,就清理剩下300M左右的堆内存了。

回到顶部