面试题
var b = [];
for(var i=0;i<15;i++){
b.push(new Array(20*1024*1024));
}
结果:
为什么我们要关注内存
- 防止页面占用内存过大,引起客户端卡顿,甚至无响应;
- Node使用的也是v8,内存对于后端服务的性能至关重要。因为服务的持久性,后端更容易造成内存溢出;
V8引擎内存回收机制
V8引擎的内存大小
- 和操作系统有关64位为1.4G,32位为0.7G;
- 64位下新生代的空间为64MB,老生代为1400MB;
- 32为下新生代的空间为16MB,老生代为700MB;
注意:为什么浏览器没有把内存设置大点?
Javascript回收内存的时候会中断执行,如果内存过大,中断时间就会过长(一般100MB会需要3ms)
v8内存分配
- 最开始的变量放在新生代的from空间;
- 如果from的占有内存容量超过新生代的25%,则将from中的变量复制到to空间,然后清空from空间中的变量;
- 如果to的占有内存容量超过新生代的25%(注意:这里不是指占新生代总内存空间的25%,而是指to空间的25%),则将to中的变量复制到from空间,然后清空from空间中的变量;
- 如果一个变量复制超过一次,并且新生代中的内存超过25%,则放入老生代;
- 老生代通过标记、删除、整理方式进行垃圾回收(为什么不用标记、清除,因为该算法造成内存空间不连续问题,但是数组必须是内存连续的);
内存如何回收
内存查看
- 浏览器 -- window.performance
- Node -- process.memoryUsage();
function getMemory(){
var memory = process.memoryUsage();
function format(bytes){
return (bytes/1024/1024).toFixed(2)+'MB'
}
console.log('headTotal: '+format(memory.heapTotal)+' headpUsed: '+format(memory.heapUsed)
);
}
var a = [];
var size = 20*1024*1024;
function b(){
var arr1 = new Array(size);
var arr2 = new Array(size);
var arr3 = new Array(size);
var arr4 = new Array(size);
var arr5 = new Array(size);
}
b();
getMemory();
setInterval(() => {
a.push(new Array(size));
getMemory();
}, 1000);
- 上图可以看出Node的内存情况
- 最开始打印出的内存使用是800MB左右;
- 然后降了,是因为内存使用量已经到达了垃圾回收的标准,回收了局部变量arr1,arr2,arr3,arr4,arr5;
- 后面内存一直往上增,是因为全局变量a的内存一直新增,但是它是一个全局变量,无法被垃圾回收,所以导致堆内存溢出;
优化内存的技巧
- 尽量不要定义全局变量
- 全局变量记得销毁掉
- 用匿名自执行函数变全局为局部
- 尽量避免闭包引用
容易引发内存使用不当的情景
- 滥用全局变量
如果一定需要使用全局变量,尽量手动回收: a = undefined; - 缓存不限制
Node缓存是个全局变量,如果一直往缓存加东西,内存会溢出,建议将缓存放redis、给缓存加锁等方式来解决;
//例如通过判断长度给缓存加锁,这样b中就一直只会有5个元素
var b = [];
for(var i=0;i<15;i++){
if(b.length>4){
b.shift();
}
b.push(new Array(20*1024*1024));
}
- 操作大文件
- 浏览器:用input上传大文件,浏览器直接卡死;
解决办法:切片上传、断点续传。file.slice(0,1000); - node:大文件读取、不能用fs.readFile(),因为这个方法是一次性读取文件到buffer;
解决办法:用流的方式读取,createReadStream().pipe(write)