js垃圾回收机制原理给你聊的明明白白

1,360 阅读6分钟

前言

大多数语言都是提供自动内存管理机制,比如C#、Java,JavaScript。自动内存管理机制也就是我们经常听到的垃圾回收机制 。好神奇哦,语言会收垃圾,哈哈😄,不过这里的垃圾,可不是家里面的厨余垃圾啥的,而是 一些不再使用的变量所占用的内存。我们的js的执行环境会自动对这些垃圾进行回收,也就是释放那些不再使用的变量所占用的内存,收垃圾的过程 会按照固定的时间间隔周期性的执行 垃圾回收

内存泄露

如果 那些不再使用的变量所占用的内存 没有被释放 会怎样呢??? 那就会造成 内存泄露

什么是内存泄露??? 别着急往下看

内存泄露其实就是我们的程序中已经动态分配的堆内存,由于某些原因没有得到释放,造成系统内存的浪费导致程序运行速度减慢甚至系统崩溃等严重后果。后果很严重的哦,现在知道为啥要有垃圾回收机制了嘛

垃圾回收机制

看看这个代码可以加深理解垃圾回收机制哦☺

let name = 'Symbol卢';
function aboutMe() {
  let hobby = '吃肉肉';
  let say=`我是${name},我喜欢${hobby}`;
  console.log(say);
}
aboutMe()

在函数 aboutMe() 执行的时候,声明了两个局部的变量 hobbysay ,等函数执行完毕之后,这两个局部变量也就不再使用 ,所以垃圾回收机制 就会将不再使用的(局部)变量 hobbysay 清除掉 (释放了它们的内存)

在js中能实现这样的垃圾回收的功能的一共有两种方式: 标记清除引用计数

标记清除

标记清除是js中最常用的垃圾回收方式。它的原理也比较好理解,废话不多说,先上代码

function speakLines(){
  let night="天黑";//做个标记 ,进入环境 
  let closeEyes="闭眼";//做个标记 ,进入环境 
  let speak=`开始狼人杀,${night}${closeEyes}`;//做个标记 ,进入环境 
  console.log(speak);
}
speakLines() //代码执行完毕  里面被标记过的变量,又被标记 离开环境 最后被回收

当代码执行在一个环境中时(例如上面的 speakLines函数),每声明一个变量,就会对该变量做一个标记(例如标记一个进入执行环境);当代码执行进入另一个环境中时,也就是要离开上一个环境( speakLines 函数执行完毕,去执行其他的函数),这时对上一个环境中的变量做一个标记,(例如标记一个离开执行环境),等到垃圾回收执行时,会根据标记来决定要清除哪些变量进行释放内存

引用计数

引用计数是一种不太常用的垃圾回收方式。同样也是先上代码再说原理

let skill = ["唱歌""弹吉他""播音"]
function change() {
  let new_skill = ["vue""react""node""webpack"".net""mysql""sqlServer""nginx"];
  skill = new_skill;
  //一个文艺小青年就这样的变成了一个技术宅男
}
change()
console.log(skill)  //返回  ["vue""react""node""webpack"".net""mysql""sqlServer""nginx"]

change()内部声明了一个局部变量 new_skill,并将一个引用类型 的数组 赋值给它,同时又将变量new_skill赋值给了全局变量 skill,此时,这个局部变量 new_skill 就不会被当成垃圾回收了。啊,为什么呢??? 还记得我们在上面说过的 垃圾回收,回收的是那些不再使用的变量,释放它们的内存,因为此时的局部变量 new_skill 并不是一个 不再使用 的局部变量了,它被全局变量 skill所引用了( 还在使用),所以就不会被回收了。

上面的这一过程,使用的就是引用计数的垃圾回收方式

引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值的引用次数减1,当这个值的引用次数变为0的时候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,当垃圾回收的时候,就会将 引用次数为0的进行回收,释放对应的内存

let skill = ["唱歌""弹吉他""播音"]
function change() {
  let new_skill = ["vue""react""node""webpack"".net""mysql""sqlServer""nginx"];//被引用次数为0 
  skill = new_skill; ////被引用次数为1
}
change()
console.log(skill) 

管理内存

在我们的项目中,我们肯定是想要用更少的内存,来使得页面有更好的性能。这个时候就需要我们来手动的管理内存了,及时的去释放数据的引用。我们可以只将需要的数据,存入变量。一旦这个数据不再使用了,我们需要手动的给变量赋值 null 来释放数据的引用。(-------解除引用) 大多需要我们进行手动的解除引用的都是一些全局的变量,因为局部的变量,在离开环境的时候就会被自动清除了。

let skill = ["唱歌""弹吉他""播音"]
function change() {
  let new_skill = ["vue""react""node""webpack"".net""mysql""sqlServer""nginx"]
  skill = new_skill;
  //一个文艺小青年就这样的变成了一个技术宅男
}
change()
console.log(skill) 
skill = null;//将不再使用的变量进行赋值 unll  来释放数据引用

change() 内部声明的局部变量 new_skill 被全局变量 skill 所引用,所以此时变量 new_skill 的引用次数为1,为了让变量 new_skill 被清除引用,在代码的最后一行,赋值一个 null 给全局变量 skill,手动解除了变量 skill 对变量 new_skill 的引用,此时变量 new_skill 的引用次数 减1,所以 当前 new_skill的引用次数为0了。当垃圾回收机制执行的时候,发现 new_skill 的引用次数为 0,就把该变量当成无用变量给清除了,释放了内存,提高了性能。

历史好文