前端小知识点:JS垃圾回收机制

Posted 前端小歌谣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端小知识点:JS垃圾回收机制相关的知识,希望对你有一定的参考价值。

目录

一、什么是垃圾回收

二.垃圾回收机制原理

三、垃圾回收方法

3.1 引用计数

3.2 标记清除

内存常见内存泄露以及解决方法

4.1 全局变量:

4.2 定时器和回调函数 

4.3 闭包 

4.4 没有清理DOM元素引用: 


一、什么是垃圾回收

垃圾回收是一种自动的内存管理机制。当计算机上的动态内存不再需要时,就应该予以释放,以让出内存。直白点讲,就是程序是运行在内存里的,当声明一个变量、定义一个函数时都会占用内存。内存的容量是有限的,如果变量、函数等只有产生没有消亡的过程,那迟早内存有被完全占用的时候。这个时候,不仅自己的程序无法正常运行,连其他程序也会受到影响。好比生物只有出生没有死亡,地球总有被撑爆的一天。所以,在计算机中,我们需要垃圾回收。需要注意的是,定义中的“自动”的意思是语言可以帮助我们回收内存垃圾,但并不代表我们不用关心内存管理,如果操作不当,javascript 中依旧会出现内存溢出的情况。

二.垃圾回收机制原理

垃圾回收基于两个原理:

1、考虑某个变量或对象在未来的程序运行中将不会被访问

2、向这些对象要求归还内存

而这两个原理中,最主要的也是最艰难的部分就是找到“所分配的内存确实已经不再需要了”。

三、垃圾回收方法

现在各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数。

3.1 引用计数

这是最初级的垃圾回收算法(老牌浏览器使用:IE)。引用计数的策略是跟踪记录每个值被使用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,这个值得引用次数就加一,如果该变量的值变成了另一个,则这个值得引用次数就减一,当这个值的引用次数为0的时候,说明没有变量在使用,这个值无法访问。由此可以将其占用的空间回收,这些垃圾回收器就会在运行时清理掉引用次数为0的值占用的空间,但这种方法容易引起内存泄漏,因为这种方式没有解决循环引用的问题,所以不建议使用!

function fun4(){
var obj = {}//引用类型变量,c的引用计数为0
var o = obj; //obj被o引用,obj的引用计数为1
var m = obj; //obj被m引用,obj的引用计数为2
o = {}  //o不再引用obj,obj的引用计数减为1
m = null //m不再引用obj,obj的引用计数减为0
}

但是引用计数存在一些问题:循环引用

function fun5(){

	var f = {};
	var g = {};
	f.userName = g;
	g.userName = f;
	//由于f和g互相引用,计数永远不可能为0

}

3.2 标记清除

这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。

垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

function fun3(){
var a = 1;
var b = 2;
//函数执行时,ab分别被标记,进入环境
}
fun3()  函数执行结束,ab被标记 离开环境 ,被回收

但是也要注意

function fun1(){
	var obj = {}
}
function fun2(){
	var obj = {}
	return obj;
}
var a = fun1();
var b = fun2();
fun1 执行时为 obj 分配了一块内存,但是随着函数执行结束,obj占用的空间也就被释放了
fun2 执行时,也为 obj 分配了内存,但是由于 obj
 最终被返回赋值给了 b 导致其依然被使用,所以 fun2 中的 obj 占用的内存不会被释放
  • 内存常见内存泄露以及解决方法

  • 4.1 全局变量:



function foo() {
  this.bar2 = '默认绑定this指向全局' // 全局变量=> window.bar2
  bar = '全局变量'; // 没有声明变量 实际上是全局变量=>window.bar
}
foo();

解决方法:在函数内使用严格模式==》严格模式禁止this关键字指向全局对象

function foo() {
  "use strict"; 
  this.bar2 = "严格模式下this指向undefined"; 
  bar = "报错";
}
foo();

4.2 定时器和回调函数 



当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调
函数以及内部依赖的变量都不能被回收,造成内存泄漏。

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        node.innerhtml = JSON.stringify(someResource));
        // 定时器也没有清除
    }
    // node、someResource 存储了大量数据 无法回收
}, 1000);

解决方法: 在定时器完成工作的时候,手动清除定时器。

4.3 闭包 



闭包可以维持函数内局部变量,使其得不到释放,造成内存泄漏。
function bindEvent() {
  var obj = document.createElement("XXX");
  var unused = function () {
      console.log(obj,'闭包内引用obj obj不会被释放');
  };
  // obj = null;
}
解决方法:手动解除引用,obj = null。

4.4 没有清理DOM元素引用: 


var refA = document.getElementById('refA');
document.body.removeChild(refA); // dom删除了
console.log(refA, "refA");  // 但是还存在引用 能console出整个div 没有被回收

解决办法:refA = null;

以上是关于前端小知识点:JS垃圾回收机制的主要内容,如果未能解决你的问题,请参考以下文章

谈谈垃圾回收机制方式内存管理?

JS(v8)垃圾回收机制

javascript中的垃圾回收机制

JVM的垃圾回收机制 总结(垃圾收集回收算法垃圾回收器)

Java GC(垃圾回收)机制知识总结

js中的垃圾回收机制