javascript垃圾收集机制的原理分析


Posted in Javascript onDecember 08, 2016

前面的话

 javascript具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。在编写javascript程序时,开发人员不用再关心内存使用问题,所需内存的分配以及无用内存的回收完全实现了自动管理。下面将详细介绍javascript的垃圾收集机制

原理

 垃圾收集机制的原理很简单:找出那些不再继续使用的变量,然后释放其占用的内存,垃圾收集器会按照固定的时间间隔,或代码执行中预定的收集时间,周期性地执行这一操作

 局部变量只在函数执行的过程中存在。而在这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储它们的值。然后在函数中使用这些变量,直到函数执行结束。此时,局部变量就没有存在的必要了。因此可以释放它们的内存以供将来使用。在这种情况下,很容易判断变量是否还有存在的必要;但并非所有情况下都这么容易就能得出结论

 垃圾收集器必须跟踪哪个变量有用哪个变量无用,对于不再有用的变量打上标记,以备将来收回其所占用的内存。用于标识无用变量的策略通常有标记清除和引用计数两种

标记清除

 javascript中最常用的垃圾收集方式是标记清除(mark-and-sweep),当变量进入环境(例如,在函数中声明一个变量),就将这个变量标记为'进入环境'。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为'离开环境'

 可以用任何方式来标记变量。比如,可以通过翻转某个特殊的位来记录一个变量何时进入环境,或者使用一个'进入环境的'变量列表以及一个'离开环境'的变量列表来跟踪哪个变量发生了变化。说到底,如何标记变量其实并不重要,关键在于采取什么策略

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

 大多数浏览器实现使用的都是标记清除式的垃圾收集策略,只不过垃圾收集的时间互有不同

引用计数

另一种不太常见的垃圾收集策略叫做引用计数(reference counting)

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1,如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值的引用的变量又取得了另外一个值,则这个值的引用次数减1,当这个值的引用次数为0时,则说明没有办法再访问这个值了,因此就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占用的内存

Netscape Navigator3.0是最早使用引用计数策略的浏览器,但很快它就遇到了一个严重的问题——循环引用:对象A中包含一个指向对象B的指针,对象B中也包含一个指向对象A的指针

function problem(){
 var objectA = new Object();
 var objectB = new Object();
 objectA.someOtherObject = objectB;
 objectB.anotherObject = objectA;
}

在这个例子中,objectA和objectB通过各自的属性相互引用,这两个对象的引用次数都是2。在采用标记清除策略的实现中,由于函数执行之后,这两个对象都离开了作用域,因此这种相互引用不是问题。但在采用引用计数策略的实现中,当函数执行完毕之后,objectA和objectB还将继续存在,因为它们的引用次数永远不会是0。假如这个函数被重复多次调用,就会导致大量内存得不到回收

IE中有一部分对象并不是原生javascript对象,例如,其BOM和DOM中的对象就是使用c++以COM(component Object Model 组件对象模型)对象的形式实现,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的javascript引擎是使用标记清除策略来实现的,但javascript访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题

var element = document.getElementById('some_element');
var myObject = new Object();
myObject.element = element;
element.someObject = myObject;

这个例子在一个DOM元素(element)与一个原生javascript对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为element的属性指向element对象,而变量element也有一个属性名为someObject的属性指向myObject。由于存在这个循环引用,即使将例子中的DOM从页面中移除,它也永远不会被回收

为了避免类似这样的循环引用,最好是在不使用它们的时候手工断开原生javascript和DOM元素之间的连接

myObject.element = null;
element.someObject = null;

将变量设置为null意味着切断变量与它此前引用的值之间的连接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存

为了解决此问题,IE9把BOM和DOM对象都转换成了真正的javascript对象

性能问题 

    垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很可观,那么回收工作量也是相当大的。在这种情况下,确定垃圾收集时间间隔是一个非常重要的问题

    IE的垃圾收集器是根据内存分配量运行的。具体一点说,就是256个变量,4096个对象(或数组)字面量和数组元素(slot)或者64kb的字符串。达到上述任何一个临界值,垃圾收集器就会运行

 这种实现方式的问题在于,如果一个脚本中包含那么多变量,那么该脚本很可能会在其生命周期中一直保有那么多的变量。而这样一来,垃圾收集器就不得不频繁地运行。结果,由此引发的严重性能问题促使IE7重写了其垃圾收集例程

 IE7的javascript引擎的垃圾收集例程改变了工作方式:触发垃圾收集的变量分配、字面量和数组元素的临界值被调整为动态修正。IE7中的各项临界值在初始时与IE6相等。如果垃圾收集例程回收的内存分配量低于15%,则变量、字面量和数组元素的临界值就会加倍。如果例程回收了85%的内存分配量,则将各种临界值重置回默认值。这样,极大地提升了IE在运行包含大量javascript的页面时的性能

 事实上,在有的浏览器中可以触发垃圾收集过程。在IE中,调用window.CollectGarbage()方法会立即执行垃圾收集

内存管理

    使用具备垃圾收集机制的javascript的主要问题在于:分配给web浏览器的可用内存数量通常要比分配给桌面应用程序的少,目的是防止运行javascript的网页耗尽全部系统内存而导致系统崩溃。内存限制问题不仅会影响给变量分配内存,同时还会影响调用栈以及在一个线程中能够同时执行的语句数量

 因此,确保占用最少的内存可以让页面获得更好的性能。而优化内存占用的最佳方式是:为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为null来释放其引用,这种做法叫解除引用(dereferencing)。这一做法适用于大多数全局变量和全局对象的属性,局部变量会在它们离开执行环境时自动被解除引用

function createPerson(name){
 var localPerson = new Object();
 localPerson.name = name;
 return localPerson;
}
var globalPerson = createPerson('test');
globalPerson = null;

   不过,要注意的是,解除一个值的引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
jQuery实现的Email中的收件人效果(按del键删除)
Mar 20 Javascript
jQuery Deferred和Promise创建响应式应用程序详细介绍
Mar 05 Javascript
利用JQuery和Servlet实现跨域提交请求示例分享
Feb 12 Javascript
js的flv视频播放器插件使用方法
Jun 23 Javascript
jQuery实现下滑菜单导航效果代码
Aug 25 Javascript
js点击按钮实现带遮罩层的弹出视频效果
Dec 19 Javascript
JS定时器用法分析【时钟与菜单中的应用】
Dec 21 Javascript
解决Vue 浏览器后退无法触发beforeRouteLeave的问题
Dec 24 Javascript
jQuery中图片展示插件highslide.js的简单dom
Apr 22 jQuery
js实现动态时钟
Mar 12 Javascript
JS+CSS+HTML实现“代码雨”类似黑客帝国文字下落效果
Mar 17 Javascript
nestjs中异常过滤器Exceptionfilter的具体使用
Feb 07 Javascript
基于JS实现的随机数字抽签实例
Dec 08 #Javascript
利用js+css+html实现固定table的列头不动
Dec 08 #Javascript
微信开发 JS-SDK 6.0.2 经常遇到问题总结
Dec 08 #Javascript
js实现百度地图定位于地址逆解析,显示自己当前的地理位置
Dec 08 #Javascript
通过Ajax使用FormData对象无刷新上传文件方法
Dec 08 #Javascript
js倒计时小实例(多次定时)
Dec 08 #Javascript
详解JavaScript中的属性和特性
Dec 08 #Javascript
You might like
CI框架源码阅读,系统常量文件constants.php的配置
2013/02/28 PHP
php新浪微博登录接口用法实例
2014/12/23 PHP
php处理json格式数据经典案例总结
2016/05/19 PHP
Ubuntu 16.04下安装PHP 7过程详解
2017/03/28 PHP
PHP chunk_split()函数讲解
2019/02/12 PHP
PHP5.6.8连接SQL Server 2008 R2数据库常用技巧分析总结
2019/05/06 PHP
php post换行的方法
2020/02/03 PHP
JS 判断undefined的实现代码
2009/11/26 Javascript
Javascript 匿名函数及其代码模式原理
2010/03/19 Javascript
JQuery实现倒计时按钮的实现代码
2012/03/23 Javascript
解析DHTML,JavaScript,DOM,BOM以及WEB标准的描述
2013/06/19 Javascript
JavaScript执行顺序详细介绍
2013/12/04 Javascript
Angularjs中UI Router的使用方法
2016/05/14 Javascript
深入理解javascript中的 “this”
2017/01/17 Javascript
Angular企业级开发——MVC之控制器详解
2017/02/20 Javascript
详解nodejs爬虫程序解决gbk等中文编码问题
2017/04/06 NodeJs
详解react内联样式使用webpack将px转rem
2018/09/13 Javascript
vue获取form表单的值示例
2019/10/29 Javascript
vue实现element表格里表头信息提示功能(推荐)
2019/11/20 Javascript
[01:16:16]DOTA2-DPC中国联赛定级赛 RNG vs Phoenix BO3第二场 1月8日
2021/03/11 DOTA
pymssql数据库操作MSSQL2005实例分析
2015/05/25 Python
PyQt5实现下载进度条效果
2018/04/19 Python
python 环境搭建 及python-3.4.4的下载和安装过程
2019/07/20 Python
Html5获取高德地图定位天气的方法
2019/12/26 HTML / CSS
苏宁红孩子母婴商城:redbaby
2017/02/12 全球购物
TripAdvisor越南:全球领先的旅游网站
2017/09/21 全球购物
Sneaker Studio法国:购买运动鞋
2018/06/08 全球购物
波兰家居和花园家具专家:4Home
2019/05/26 全球购物
定制别致的瑜伽垫:Sugarmat
2019/06/21 全球购物
岗位聘任书范文
2014/03/29 职场文书
巾帼文明岗申报材料
2014/05/01 职场文书
鼓舞士气的口号
2014/06/16 职场文书
爱护公物演讲稿
2014/09/09 职场文书
企业总经理助理岗位职责
2014/09/12 职场文书
MySQL 覆盖索引的优点
2021/05/19 MySQL
css中有哪些方式可以隐藏页面元素及区别
2022/06/16 HTML / CSS