内存大小和操作系统有关: 64位-1.4G 32位-0.7G
64位系统下的新生代内存-64MB 老生代内存-1400MB
32位系统下的新生代内存-16MB 老生代内存-700MB
为什么限制内存大小?
1、够用了,js设计的初衷是浏览器脚本语言,只执行一次,便释放内存
2、如果不限制,回收一次100MB的内存大概花费3ms,V8引擎在回收垃圾时,是暂停住所有代码的执行,一旦垃圾过多,在回收内存的时候,中断的时间过长,体验不佳。
新生代内存:保存新变量,存活时间短的
老生代内存:保存老变量,存活时间长的(老变量:经过几次垃圾回收也没有被杀死的变量)
新生代变量晋升老生代变量的条件:①这个变量经历过内存回收,当未被回收 ②新生代内存空间一旦超过占用临界值,①中的变量就会被移到老生代内存中
新生代内存特点:内存回收频率快,因为频率快,所以要求回收这个动作要快,即持续时间短,所以采用了牺牲空间换取时间的算法。
新生代内存回收机制
假设所有新变量都存在From中,那么在回收的过程中,会先标记活变量,将活着的变量全部复制到To当中,清空From,第二次回收时,标记活变量,将活着的变量全部复制到From当中,清空To 也就是:标记-复制-清空的过程
老生代内存回收机制
假设在内存中有一片连续的区域:1,2,3,4,5 此时2,4死亡,标记并删除后,内存变为:1,-,3,-,5 这时内存空间会不连续,也就是所谓的内存碎片,如果不进行修补,有可能会放不进数组,因为在数据结构中,数组中的元素是储存在内存中的连续一片地址中的。所以在V8引擎中,老生代内存在标记-删除之后,还要进行一次排列,整理内存碎片。
标记死亡变量,删除死亡变量,整理内存空间 也就是标记-删除-整理的过程
新生代内存:标记-复制-清空(省时,费空间) 老生代内存:标记-删除-整理
内存回收时机
在内存占用接近临界值的时候,开始回收内存
全局变量:只在程序执行完成才会被回收,否则永久保留
查看内存的方法:
浏览器: window.performance
一段代码:Node-process.memoryUsage()
function getMemory() {
var memory = process.memoryUsage();
var format = function(bytes){
return (bytes/1024/1024).toFixed(2)+"MB"
}
console.log('heapTotal'+ format(memory.heapTotal)+ ' heapUsed'+ format(memory.heapUsed))
}
查看内存使用情况
function getMemory() {
var memory = process.memoryUsage();
var format = function(bytes){
return (bytes/1024/1024).toFixed(2)+"MB"
}
console.log('heapTotal'+ format(memory.heapTotal)+ ' heapUsed'+ 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)
}
b()
getMemory()
setInterval(()=>{
a.push(new Array(size))
getMemory()
},1000)
在控制台中执行 node xx.js 便可看到内存的变化情况
容易引发内存使用不当的情景:①滥用全局变量 ②缓存不限制 ③操作大文件
全局变量能不用就不用,如果必须要用,就要在用完之后释放掉内存,不用delete,因为在strict模式下会有意想不到的bug,直接将该全局变量置为undefined
一般缓存也是定义一个全局变量,将一些数据放到缓存对象中,这是一般做法,但是如果是个持久化的操作,会不断往缓存中放东西,也会有内存不足的危险。所以要给缓存加上限制。
不给缓存加上限制的情景:
var a = []
var size = 30 * 1024 * 1024
for (let i = 0; i < 16; i++) {
a.push(new Array(size))
}
其中 a 为缓存对象,size是模拟数组的大小,这是循环往a中不断放一个超大数组,在16次之内,已经能造成内存不足,导致程序无法进行下去了。
给缓存加上限制:
var a = []
var size = 30 * 1024 * 1024
for (let i = 0; i < 16; i++) {
if(a.length < 4){
a.shift()
}
a.push(new Array(size))
}
在不断往a中放数据的时候,加上判断,如果a中超过4个元素,就将第一个元素去掉这样就保证缓存不会一直在变大。
性能优化
lighthouse:谷歌的一个插件
回报代码:window.performance 中有详细的性能数据信息,可以在公共模板中使用这些数据,再通过ajax或其他方式发送给后端的某个监听接口,有后端来让这些数据可视化。