宝哥软件园

向我学习javascript的垃圾收集机制和内存管理

编辑:宝哥软件园 来源:互联网 时间:2021-09-22

一、垃圾回收机制——GC。

Javascript具有自动垃圾收集机制(GC :垃圾收集),即执行环境负责管理代码执行过程中使用的内存。

原理:垃圾收集器会定期(周期性)找出不再使用的变量,然后释放内存。

JavaScript的垃圾收集机制很简单:找出不再使用的变量,然后释放它们占用的内存。但是,这个过程不是实时的,因为它的开销很高,所以垃圾收集器会以固定的时间间隔定期执行。

不再使用的变量是生命周期结束的变量,但当然只能是局部变量。在浏览器卸载页面之前,全局变量的生命周期不会结束。局部变量只在函数执行过程中存在,在这个过程中,在栈或堆上为局部变量分配相应的空间来存储它们的值,然后这些变量在函数中使用,直到函数结束,而闭包中的外部函数由于内部函数的原因不能被视为结束。

或者在代码描述上:

函数fn1(){ var obj={ name : ' hanzichi ',age : 10 };} function fn2(){ var obj={ name : ' hanzichi ',age : 10 };返回对象;} var a=fn1();var b=fn2();让我们看看代码是如何执行的。首先定义了两个函数,分别叫做fn1和fn2。调用fn1时,会打开一个内存存储对象{name:' hanzichi ',age:10},调用后从fn1出来时,内存会被js引擎中的垃圾收集器自动释放。在调用fn2的过程中,返回的对象被全局变量b指向,所以不会释放内存块。

问题来了:哪个变量没用?因此,垃圾收集器必须跟踪哪些变量是无用的,并标记那些不再有用的变量,以便将来回收它们的内存。标记无用变量的策略可能因实现而异。通常有两种实现:标记清除和引用计数。参考计数不太常见,但标记清除更常见。

第二,去除标记。

js中最常用的垃圾收集方法是标签移除。当变量进入环境时,例如,当变量在函数中声明时,该变量被标记为“进入环境”。从逻辑上讲,进入环境的变量所占用的内存是永远无法释放的,因为只要执行流进入相应的环境,它们就可能被使用。当变量离开环境时,它被标记为“离开环境”。

函数测试(){ var a=10//被标记,进入var b=20的环境;//被标记并进入环境} test();//执行后,A、B标记出环境,回收。当垃圾收集器运行时,它将标记存储在内存中的所有变量(当然,可以使用任何标记方法)。然后,它删除环境中的变量和环境中的变量引用的变量的标签(闭包)。之后,标记的变量将被视为要删除的变量,因为环境中的变量不再能够访问这些变量。最后,垃圾收集器完成内存清理,销毁那些标记的值并回收它们占用的内存空间。

到目前为止,IE、Firefox、Opera、Chrome和Safari的js实现都使用了标签移除的垃圾收集策略或者类似的策略,但是垃圾收集的时间间隔各不相同。

第三,参考计数。

引用的含义是跟踪每个值被引用的次数。当一个变量被声明并且引用类型值被赋给该变量时,该值的引用数为1。如果将相同的值赋给另一个变量,则该值的引用数将增加1。相反,如果包含对该值的引用的变量取另一个值,对该值的引用数将减少1。当这个值的引用数变成0时,意味着没有办法访问这个值,所以它占用的内存空间可以回收。这样,当垃圾收集器下次再次运行时,它将释放引用次数为0的值所占用的内存。

函数测试(){ var a={ };//a的引用次数为0 var b=a;//a的引用次数增加1,表示1 var c=a;//a的引用次数增加1,为2 var b={ };//的引用数减少1到1。} Netscape Navigator 3是第一个使用引用计数策略的浏览器,但它很快就遇到了一个严重的问题:循环引用。循环引用意味着对象a包含指向对象b的指针,而对象b也包含对对象a的引用.

函数fn(){ var a={ };var b={ };a . pro=b;b . pro=a;} fn();以上代码A和B的参考时间均为2。fn()执行后,两个对象都离开了环境,所以标签清除模式没有问题。但是在引用计数策略下,由于代码A和B的引用次数都不是0,所以不会被垃圾收集器回收。如果大量调用fn函数,会造成内存泄漏。在IE7和IE8上,内存线性上升。

我们知道,IE中的一些对象不是原生js对象。比如DOM和BOM中的对象是用C语言以COM对象的形式实现的,COM对象的垃圾收集机制采用引用计数策略。因此,即使IE的js引擎实现了标签清除策略,js访问的COM对象仍然基于引用计数策略。换句话说,只要IE中涉及COM对象,就会出现循环引用的问题。

var element=document . getelementbyid(' some _ element ');var myObject=new Object();myObject.e=元素;element.o=myObject这个例子在一个DOM元素和一个本地js myObject之间创建了一个循环引用。变量myObject有一个名为element的属性指向元素对象;变量元素还有一个名为o回指myObject的属性。由于这种循环引用,即使从页面中移除了示例中的DOM,它也永远不会被回收。

看上面的例子,有些同学觉得自己太弱了,谁会做这么无聊的事情,其实我们在做吗?

window . onload=function outfunction(){ var obj=document . getelementbyid(' element ');obj . onclick=function innerFunction(){ };};这段代码看起来没什么问题,但是obj指的是document.getelementbyid(“元素”),document.getelementbyid(“元素”)的onclick方法指的是外部环境中的德语变量,自然也包括obj。是不是很隐蔽?

解决办法

最简单的方法就是手动取消循环引用,比如刚才的函数就可以做到这一点。

myObject.element=nullelement.o=null

window . onload=function outfunction(){ var obj=document . getelementbyid(' element ');obj . onclick=function innerFunction(){ };obj=null};将变量设置为null意味着将变量与其先前引用的值断开。当垃圾收集器下次运行时,它将删除这些值并回收它们占用的内存。

需要注意的是,IE9不存在循环引用导致的Dom内存泄漏问题。可能是微软做了优化,也可能是Dom的回收方式变了。

第四,内存管理。

1.垃圾收集什么时候触发?

垃圾收集器定期运行。如果分配的内存非常大,恢复工作会非常困难,因此确定垃圾收集时间间隔值得考虑。IE6的垃圾收集根据内存分配运行。当环境中有256个变量、4096个对象、64k个字符串时,就会触发垃圾收集器工作。看起来很科学,没必要一段时间不按就叫一次。按需调用不好吗?但是如果环境中有那么多变量,而脚本现在又那么复杂正常,那么结果就是垃圾收集器一直在工作,所以浏览器无法播放。

微软在IE7中做了调整,触发条件不是固定的,而是动态修改的。初始值与IE6相同。如果垃圾收集器分配的内存少于程序占用内存的15%,意味着大部分内存无法回收,垃圾收集设置的触发条件过于敏感。此时,面向街道的条件加倍。如果恢复的内存高于85%,则意味着大部分内存应该在很久之前就被清理了。这时,把触发条件放回去。这使得垃圾收集功能很多。

2.合理的GC方案。

Javascript引擎的基本GC方案是(简单GC):标记并扫取,即:

(1)遍历所有可访问的对象。(2)回收不可访问的对象。2)气相色谱的缺陷。

和其他语言一样,javascript的GC策略也无法避免一个问题:GC期间停止响应其他操作,这是出于安全考虑。而Javascript的GC在100ms以上,对于一般应用来说是不错的,但是对于JS游戏来说,对于动画连贯性要求高的应用就麻烦了。这就是新发动机需要优化的地方:避免GC造成的长时间停机响应。

3) GC优化策略。

大卫大叔主要介绍了两个优化方案,这是最重要的两个优化方案:

(1)代GC与Java回收策略一致。目的是区分“暂时”和“永久”对象;回收更多的“年轻一代”区域和更少的“持久一代”区域减少了每次需要遍历的对象数量,从而减少了每次GC的时间消耗。图片:

这里需要补充的是,租用代对象有额外的开销:从年轻一代迁移到租用代,如果被引用,需要修改引用点。

(2)增量GC的思路很简单,就是“一次一点,下次一点,以此类推”。图片:

该方案虽然耗时短,但中断次数多,带来了频繁上下文切换的问题。

因为每个方案都有自己的适用场景和缺点,在实际应用中,会根据实际情况来选择方案。

例如,当(对象/s)比率较低时,中断GC执行的频率低于简单GC。如果大量对象长期“活着”,分代处理的优势就不大了。

参考:

以上都是关于javascript的垃圾收集机制和内存管理,希望对大家的学习有所帮助。

更多资讯
游戏推荐
更多+