V8是Node的JavaScript执行引擎,V8引擎实际是一个高性能虚拟机。Node在JavaScript的执行直接受益于V8,可以随着V8的升级就能享受更好的性能或新的语言特性(如ES5和ES6)
说明:基于V8这种限制将会导致Node无法操作大内存对象,也因此后来出现了buffer这种不受V8丢内存控制的堆外内存管理。
由于V8已经对内存做了限制,我们应该做到高效的使用内存,让垃圾回收机制更高效的工作,避免一些不容易回收内存的出现。
在JavaScript中,能形成作用域的有函数,with以及全局作用域。
说明:虽然两种方式都可以主动释放变量引用的对象(也就是那一小块内存),但是推荐大家使用重新赋值的方法,因为在V8中通过delete删除对象的属性有可能干扰V8的优化。
在javaScript中,实现外部作用域访问内部作用域中变量的方法叫做闭包(closure)。这得益于高阶函数的特性:函数可以作为参数或者返回值。闭包它实现了外部作用域访问内部作用域中变量的方法。这句话需要好好理解。
简单例子说明闭包 两段代码对比:
var A=function(){
(function(){
var local="局部变量";
}());
console.log(local); //local未定义异常
}
var B=function(){
var C=function(){
var local="局部变量";
return function(){
return local;
};
};
var c=C();
console.log(c()); //局部变量
};
分析第二段代码,函数C执行完成后,局部变量local会随着作用域的销毁而被回收。但是注意这里的特点是返回值是一个匿名函数,而且这个函数中具备了访问local的条件,后面的代码执行,外部作用域是无法直接访问local的,但是若要访问它,只要通过这个中间函数稍作周转即可。以上就是闭包的基本分析,现在能够更好的理解我画重点的那句话了吧。
对于闭包的详细介绍,大家可以看这篇文章“https://juejin.im/post/5cf2515af265da1b6720f627”(明天公众号会发闭包的详解与面试相关内容)
$ node
> process.memoryUsage();
{
rss:14958592,
heapTotal:7195904,
heapUsed:2821496
}
heapTotal:V8中已申请的堆内存
heapUsed:V8中当前使用的堆内存
rss:进程的常驻内存部分
$ node
> os.totalmem()
82132131
> os.freemem()
31273127
os.totalmem 操作系统的总内存
os.freemem 操作系统的闲置内存
查看v8内存使用情况,process.memoryUsage()的结果可以看到,V8堆中的内存用量总是小于进程的常驻内存用量rss,也就是说Node中的内存使用并非都是V8控制,还有一部分不是通过V8分配的(rss-heaptotal这部分),不通过V8分配的内存称之为堆外内存。
使用buffer每次构造200MB的内存,代码如下:
var useMem=function(){
var size=200*1024*1024;
var buffer=new Buffer(size);
for(var i=0;i<size;i++){
buffer[i]=0
}
return buffer;
};
代码执行过程中,查看内存使用情况会发现到最后,V8的使用内存heapUsed和申请的内存heaptotal基本不变,而常驻内存rss在不断增加,可以看出buffer对象不同于其它对象,不经过V8内存分配机制,不会有堆内存的限制。后面的文章会对buffer进行详细的讲解。
Node对内存泄漏十分敏感,一旦线上应用有成千上万的流量,哪怕一个字节的内存泄漏也会造成堆积,垃圾回收过程中将会耗费更多时间进行对象扫描,应用响应缓慢,直到进程内存溢出,应用奔溃。
应当回收的对象出现意外而没有被回收,变成常驻在老生代中的对象。