V8引擎-内存知识

444 阅读4分钟

1.作为前端开发,为什么要关注内存?

  • 防止页面占用内存过大,引起客户端卡顿,甚至无响应
  • Node使用的也是v8引擎,内存对于后端服务的性能至关重要,因为服务的持久性,后端更容易造成内存溢出

2.V8引擎的内存回收机制


内存大小:

  • 总内存和操作系统有关 64位 为1.4G,32位 为0.7G
  • 64位下新生代的空间为64MB,老生带为1400MB
  • 32位下新生代的空间为16MB,老生代为700MB



提问:为什么v8引擎对自己的内存大小进行限制呢?

回答:

  1. js设计之初是为了浏览器,前端的特点-不持久化,执行一遍就全部会回收了,1.4完全足够用了
  2. js回收内存的时候,会暂停执行,回收一次100MB,大概需要6ms,设计太大,会造成明显卡顿


3.垃圾回收算法:

  1. 新生代:存放一些比较小而且存放时间比较少的变量,最开始的时候,新生代的变两个会全部放在(From)这个空间内,当我们  时,新生代内存需要回收时,会把还活着的变量复制到(To)空间内,把(From)空间内的内容全部清空,这样做的目的是用空间来换时间,总是有一半的空间是空置的
  2. 老生代:不能采用新生代的算法,因为老生代的空间过大,会造成大量内存空间的浪费。它将会使用标记删除整理的算法。如下图所示,假设黑色的都是被标记需要删除的变量,这时,V8引擎会将黑色的进行清空,将白色可用的内存块往前移,这样做的目的是让可用内存空间变大,防止数组在可用内存中存放不了(因为数组必须是连续内存空间!)



4.新生代如何晋升到老生代:




5.利用node来查看内存使用情况:

function getMe(){
    var mem = process.memoryUsage()
    var format = (bytes) => {
        return (bytes/1024/1024).toFixed(2) + 'MB'
    }
    console.log('Process: heapTotal ' + format(mem.heapTotal) + 
    ' heapUsed ' + format(mem.heapUsed) + ' rss ' + format(mem.rss))
}


变量处理

  • 内存主要就是存储变量等数据的
  • 局部变量当程序执行结束,且没有引用的时候就会随着消失
  • 全局对象会始终存活到程序运行结束


体验一下撑爆v8引擎,代码如下:

var size = 20*1024*1024
var arr = new Array(size)
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)
var arr6 = new Array(size)
var arr7 = new Array(size)
var arr8 = new Array(size)
var arr9 = new Array(size)
var arr10 = new Array(size)
var arr11 = new Array(size)
var arr12 = new Array(size)
var arr13 = new Array(size)
var arr14 = new Array(size)

直接在node中运行该代码,v8引擎就会报错,内存溢出

为了看的更加仔细,可以在每个arr后面都调用getMe()方法,查看每次声明之后的内存使用情况


体验一下v8引擎内存回收带来的卡顿,代码如下:

var size = 20*1024*1024
var a = []
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()
for(var i = 0; i < 13; i++) { 
    a.push(new Array(size))
    getMe()
}

直接在node运行该代码,可以 看到:

在红框标注的地方,就进行了内存回收,会感觉到一丝丝卡顿,因为v8回收内存的时候,会暂停执行,回收一次100MB,大概需要6ms


6.如何注意内存的使用


优化内存的技巧:

  • 尽量不要定义全局变量
  • 全局变量记得销毁掉(怎么删除?直接将全局变量赋值为undefined或者null,扩展:null是一个保留字,undefined是一个特殊一点的变量)
  • 用匿名函数自调将全局变量变为局部变量
  • 尽量避免闭包(错误的理论,只有在外部引用的时候,才会有可能造成内存泄漏)

防止内存泄漏:

  • 滥用缓存(在写服务端代码时,尽量不要使用js变量来缓存,可以使用redis等来代替。如果一定要用js变量缓存,则需要加锁,在增加缓存之前,判断一下该变量的长度是否超过一定值,超多的话,就把第一个缓存进来的数据清空,遵循先进先出原则,这样做会导致缓存不完整,但是至少不会让服务器宕机)
  • 大内存量操作(node操作文件时比如fs.readFile ,该api会一次性读取文件到buffer,如果文件非常大,就会造成内存泄漏,所以类似这样的操作,需要优化成:createReadStream().pipe(write),创建读取文件流,一点一点读取文件内容;浏览器上传大文件时,也会存在内存泄漏的问题,这个时候就要用到切片上传,使用file.slice()