和C#、Java一样JavaScript有自动垃圾回收机制,也就是说执行环境会负责管理代码执行过程中使用的内存,在开发过程中就无需考虑内存分配及无用内存的回收问题了。JavaScript垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是时时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。

变量生命周期

有同学看了上面就会问了,什么叫不再使用的变量?不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量,全局变量的生命周期直至浏览器卸载页面才会结束。局部变量只在函数的执行过程中存在,而在这个过程中会为局部变量在栈或堆上分配相应的空间,以存储它们的值,然后再函数中使用这些变量,直至函数结束(闭包中由于内部函数的原因,外部函数并不能算是结束,了解闭包可以看看 JavaScript作用域链JavaScript 闭包究竟是什么)。

一旦函数结束,局部变量就没有存在必要了,可以释放它们占用的内存。貌似很简单的工作,为什么会有很大开销呢?这仅仅是垃圾回收的冰山一角,就像刚刚提到的闭包,貌似函数结束了,其实还没有,垃圾回收器必须知道哪个变量有用,哪个变量没用,对于不再有用的变量打上标记,以备将来回收。用于标记无用的策略有很多,常见的有两种方式

标记清除(mark and sweep)

这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。至于怎么标记有很多种方式,比如特殊位的反转、维护一个列表等,这些并不重要,重要的是使用什么策略,原则上讲不能够释放进入环境的变量所占的内存,它们随时可能会被调用的到。

垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了,因为环境中的变量已经无法访问到这些变量了,然后垃圾回收器相会这些带有标记的变量机器所占空间。

大部分浏览器都是使用这种方式进行垃圾回收,区别在于如何标记及垃圾回收间隔而已,只有低版本IE,不出所料,又是IE。。。

引用计数(reference counting)

在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。

看起来也不错的方式,为什么很少有浏览器采用,还会带来内存泄露问题呢?主要是因为这种方式没办法解决循环引用问题。比如对象A有一个属性指向对象B,而对象B也有有一个属性指向对象A,这样相互引用

function test(){
var a={};
var b={};
a.prop=b;
b.prop=a;
}

这样a和b的引用次数都是2,即使在test()执行完成后,两个对象都已经离开环境,在标记清除的策略下是没有问题的,离开环境的就被清除,但是在引用计数策略下不行,因为这两个对象的引用次数仍然是2,不会变成0,所以其占用空间不会被清理,如果这个函数被多次调用,这样就会不断地有空间不会被回收,造成内存泄露。

在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的,也就是说只要涉及BOM及DOM就会出现循环引用问题。看上面的例子,有同学回觉得太弱了,谁会做这样无聊的事情,其实我们是不是就在做

window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
};

这段代码看起来没什么问题,但是obj引用了document.getElementById("element"),而document.getElementById("element")的onclick方法会引用外部环境中德变量,自然也包括obj,是不是很隐蔽啊。

解决办法

最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样

window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
obj=null;
};

什么时候触发垃圾回收

垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。IE6的垃圾回收是根据内存分配量运行的,当环境中存在256个变量、4096个对象、64k的字符串任意一种情况的时候就会触发垃圾回收器工作,看起来很科学,不用按一段时间就调用一次,有时候会没必要,这样按需调用不是很好吗?但是如果环境中就是有这么多变量等一直存在,现在脚本如此复杂,很正常,那么结果就是垃圾回收器一直在工作,这样浏览器就没法儿玩儿了。

微软在IE7中做了调整,触发条件不再是固定的,而是动态修改的,初始值和IE6相同,如果垃圾回收器回收的内存分配量低于程序占用内存的15%,说明大部分内存不可被回收,设的垃圾回收触发条件过于敏感,这时候把临街条件翻倍,如果回收的内存高于85%,说明大部分内存早就该清理了,这时候把触发条件置回。这样就使垃圾回收工作职能了很多。

同C# 、Java一样我们可以手工调用垃圾回收程序,但是由于其消耗大量资源,而且我们手工调用的不会比浏览器判断的准确,所以不推荐手工调用垃圾回收。


原文链接:https://www.cnblogs.com/dolphinX/p/3348468.html

js垃圾回收(转的更多相关文章

  1. 前端面试:谈谈 JS 垃圾回收机制

    摘要: 不是每个人都回答的出来... 最近看到一些面试的回顾,不少有被面试官问到谈谈JS 垃圾回收机制,说实话,面试官会问这个问题,说明他最近看到一些关于 JS 垃圾回收机制的相关的文章,为了 B 格 ...

  2. python垃圾回收机制:引用计数 VS js垃圾回收机制:标记清除

    js垃圾回收机制:标记清除 Js具有自动垃圾回收机制.垃圾收集器会按照固定的时间间隔周期性的执行. JS中最常见的垃圾回收方式是标记清除. 工作原理 当变量进入环境时,将这个变量标记为"进入 ...

  3. v8垃圾回收和js垃圾回收机制

    垃圾回收器是一把十足的双刃剑.好处是简化程序的内存管理,内存管理无需程序员来操作,由此也减少了长时间运转的程序的内存泄漏.然而无法预期的停顿,影响了交互体验.本文从 V8 (node.js runti ...

  4. js垃圾回收及内存泄漏

    js垃圾回收 js能够自动回收申请却未使用的内存,由于每次清除需要的性能较大,不是时时在刷新,而是每隔一段时间才进行一次. 回收的两种方式 标记清除(常用) 在内存中先标记变量,然后清除那些那些进入环 ...

  5. js垃圾回收与内存泄漏

    js垃圾回收机制 概念: javascript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中的使用的内存.而在C和C++之类的语言中,开发人员的一项基本任务就是手动跟踪内存的使用情况 ...

  6. js垃圾回收和内存泄漏

    js垃圾回收和内存泄漏 js垃圾回收 Js具有自动垃圾回收机制.垃圾收集器会按照固定的时间间隔周期性的执行. 1.标记清除(常用) 工作原理:是当变量进入环境时,将这个变量标记为"进入环境& ...

  7. js 垃圾回收 & js GC

    js 垃圾回收 & js GC js GC / js Garbage Collector https://developer.mozilla.org/en-US/docs/Web/JavaSc ...

  8. 谈谈 JS 垃圾回收机制

    谈谈 JS 垃圾回收机制 JS内存泄漏与垃圾回收机制 https://javascript.info/garbage-collection

  9. 浅尝js垃圾回收机制

    局部作用域内的变量,在函数执行结束之后就会被js的垃圾回收机制销毁   为什么要销毁局部变量? => 为了释放内存   js垃圾回收机制何时会销毁局部变量 : 如果局部变量无法再得到访问,就会被 ...

  10. 闭包内的微观世界和js垃圾回收机制

    一.什么是闭包? 官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话 ...

随机推荐

  1. MongoDB 入门篇

    1.1 数据库管理系统 在了解MongoDB之前需要先了解先数据库管理系统 1.1.1 什么是数据? 数据(英语:data),是指未经过处理的原始记录. 一般而言,数据缺乏组织及分类,无法明确的表达事 ...

  2. 解决Failure to transfer org.apache.maven.plugins:maven-surefire-plugin:pom:2.17 问题

    详细报错如下 Failure to transfer org.apache.maven.plugins:maven-surefire-plugin:pom:2.17 from http://maven ...

  3. C# Serializable

    System.SerializableAttribute 串行化是指存储和获取磁盘文件.内存或其他地方中的对象.在串行化时,所有的实例数据都保存到存储介质上,在取消串行化时,对象会被还原,且不能与其原 ...

  4. spring boot零碎知识点待补充

    @Controller 和@RestController的区别 @RestController相当于同时使用了@Controller和@ResponseBody  即不会使用视图解析器,返回值直接返回 ...

  5. ArcGIS 常见错误

    1. 平面坐标转为大地坐标出错 提示是:找不到相关的要素 2. 当发现Shape文件或者Mdb内的文件有问题时,可以先将Shape文件导出,然后再导入,或许就可以解决其中的问题.

  6. docker 系列 - 企业级私有镜像仓库Harbor部署(转载)

     本文转载自 搜云库 的文章 https://www.jianshu.com/p/7d76850de03f  , 感谢作者 3.png 上一篇文章搭建了一个具有基础功能,权限认证.TLS 的私有仓库, ...

  7. DataTabe使用Linq实现 Group

    DataTable dt = dataSet.Tables[]; var query = from t in dt.AsEnumerable() group t by new { t1 = t.Fie ...

  8. APPLE-SA-2019-3-25-3 tvOS 12.2

    APPLE-SA-2019-3-25-3 tvOS 12.2 tvOS 12.2 is now available and addresses the following: CFStringAvail ...

  9. [C++]环状序列(CircularSequence,ACM/ICPC Seoul 2004,UVa1584)

    Question 例题3-5 环状序列(CircularSequence,ACM/ICPC Seoul 2004,UVa1584) 长度为n的环状串有n种表示方法,分别为从某个位置开始顺时针得到,在这 ...

  10. call apply bind 区别?

    call apply bind 区别? 例:定义一个计算器,没绑定bind的为公共计算器,call可以调用,绑定bind的为私人计算器,别人调用不了, //ps:用bind绑定的call强制作借用不好 ...