内存模型
- 堆(引用数据类型:Object,Array)
- 栈(基本数据类型: undefined,Null,String,Boolean,Number)
- 池(const)
内存的生命周期
- 栈:先进后出,后进先出
js中模仿堆栈的情况:
// 堆栈
let arr = new Array()
arr.push(1) // 从前往后插入
arr.push(2)
arr.push(3)
arr.pop()
console.log(arr) // [1,2]
实际的业务场景: 例如qq的登陆系统,如果登陆时间间隔过长,则会把登陆时间越长的用户按时间的长短回收账号,时间越长,回收的可能性越大。
- 队列: 先进先出,后进后出
js模仿队列的情况:
// 队列
let arr = new Array()
arr.unshift(1) // 从前往后插入
arr.unshift(2)
arr.unshift(3)
arr.pop()
console.log(arr) // [2,3]
实际的业务场景: 例如报名系统,只有100个名额,但是有200人报名,这个时候就要把后面报名的人去掉,只取前面报名的的100人。
内存泄漏经典场景
- 变量定义在全局环境中
function fn2(){
this.demo = 2
}
fn2() // 这个发生了内存泄漏,因为此时这种情况下的调用this指向全局变量window,demo就泄漏在全局环境中,而全局环境中的变量是不会内垃圾回收
- 定时器
// 虽然点击按钮后清除的dom,但是定时器并没有被清除,所以依然可以继续打印出时间,那样就会一直占用内存,如果这时候将时间设置成更长,那么内存会被一直占用这,页面容易发生卡顿的情况
window.onload = function () {
let domp = document.getElementById('domp')
setInterval(()=>{
let date = new Date()
if (dom) {
domp.innerHTML = JSON.stringify(date)
}
console.log(date)
},1000)
}
let dom = document.getElementById("dom")
dom.onclick = function () {
domp.remove()
}
解决方案: 将定时器在合适时机清除
// 解决方案:在合适的时间显示移除这个定时器
let timeer
window.onload = function () {
let domp = document.getElementById('domp')
timeer = setInterval(()=>{
let date = new Date()
if (dom) {
domp.innerHTML = JSON.stringify(date)
}
console.log(date)
},1000)
}
let dom = document.getElementById("dom")
dom.onclick = function () {
// 这里显示移除定时器
clearInterval(timeer)
domp.remove()
}
- 闭包:闭包会使内存长期持有变量,容易产生卡顿的问题
function FF () {
this.name = 'kkkkkk'
this.length = 180
}
FF.prototype.selectName = function () {
// 闭包会使内存长期持有_this这个变量,容易产生卡顿的问题
let _this = this
return function(){
return _this.name
}
}
解决方案:将变量用另一个变量保存起来,在合适时机清空
// 解决方案,将let name = _this.name复制给一个变量,最后把这个name=null 清空,内存就会释放
function FF () {
this.name = 'kkkkkk'
this.length = 180
}
FF.prototype.selectName = function () {
let name = this.name
return function(){
return name
}
name = null
}
通过nodejs的memoryUsage()函数实时监听内存的使用情况
console.log(process.memoryUsage())
垃圾回收机制
以下例子的情况是否会回收o?
// 垃圾回收
// 以下这种情况,o不会被回收,因为变量之间存在引用
let o ={
a: {
b:2
}
}
// 这时进行以下操作
let o2 = o // o2变量是o的引用
let oa = o2.a
o = 1 // 此时o是一个零引用的情况,那是否会被回收?答案是不会,因为内部还存在这对内部属性a的引用
o2 = 'wdfsalkg'
o = null // 要想对o进行回收,就要给o致空,释放内存
console.log(process.memoryUsage())
未置空前内存占用情况:
结论:要减少内存泄漏的要注意的点:
- 尽量少定义全局变量
- 定时器不用时要几时清除
- 闭包中使用的变量在特定时机置空
垃圾回收算法(还需要完善留坑)
- 初级回收算法 垃圾回收算法只要依赖于引用的概念。在内存管理的环境中,一个对象如果有访问另一个对象的权限(显式或隐式),叫做一个对象引用另一个对象。例如,一个js对象具有对他原型的 引用(隐式)和对他属性的引用(显式)
缺点:不能正确回收循环引用。
- 现在的垃圾回收算法(标记-清除算法) 设置一个叫做根(root)的对象(在js中,跟是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象。。。从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。 缺点:无法从根查询到的对象将被清除(可被忽略)