Javascript入门学习笔记之垃圾回收机制

      首先,我们需要对内存管理概念有一个基本的认识。内存管理是指操作系统如何进行内存的分配和回收的机制。早期的计算机语言,比如C, 它通过malloc, free函数来向操作系统请求内存和释放内存。优点是内存分配和释放的效率很高;缺点主要表现在对于复杂的系统,存在着大量的内存分配和释放操作。程序员很容易不小心忘记释放内存,从而造成内存的泄露,对于长期运行的软件来讲,系统的内存会逐渐被吃光。 因此,更新的编程语言,比如JAVA, C#, 都提供了所谓“垃圾回收的机制”,运行时自身会运行相应的垃圾回收机制。程序员只需要申请内存,而不需要关注内存的释放。垃圾回收器(GC)会在适当的时候将已经终止生命周期的变量的内存给释放掉。(https://www.zhihu.com/question/19831128/answer/13097702)

几种垃圾回收机制算法

1.引用计数算法(Reference Counting)

      每个对象计算指向它的指针的数量,当有一个指针指向自己时计数值加1;当删除一个指向自己的指针时,计数值减1,如果计数值减为0,说明已经不存在指向该对象的指针了,所以它可以被安全的销毁了。算法特点:1)需要单独的字段存储计数器,增加了存储空间的开销;2)每次赋值都需要更新计数器,增加了时间开销;3)垃圾对象便于辨识,只要计数器为0,就可作为垃圾回收;4)及时回收垃圾,没有延迟性;5)不能解决循环引用的问题。

      在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。比如对象A有一个属性指向对象B,而对象B也有有一个属性指向对象A,这样相互引用

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

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

2. 标记-清除(Mark-Sweep)算法

      Mark-Sweep依赖于对所有存活对象进行一次全局遍历来确定哪些对象可以回收,遍历的过程从根出发,找到所有可达对象,除此之外,其它不可达的对象就是垃圾对象,可被回收。整个过程分为两个阶段:标记阶段找到所有存活对象;清除阶段清除所有垃圾对象。Lua就采用了mark-sweep的垃圾回收机制。大部分浏览器也都是使用这种方式进行垃圾回收,区别在于如何标记及垃圾回收间隔而已 。

优点:

相比较引用计数算法,标记-清除算法可以非常自然的处理环形引用问题,

另外在创建对象和销毁对象时时少了操作引用计数值的开销

缺点:

标记-清除算法是一种“停止-启动”算法,在垃圾回收器运行过程中,应用程序必须暂时停止

标记-清除算法在标记阶段需要遍历所有的存活对象,会造成一定的开销

在清除阶段,清除垃圾对象后会造成大量的内存碎片。

3. 标记-缩并算法(解决内存碎片)

整个过程可以描述为
* 标记所有的存活对象;
* 通过重新调整存活对象位置来缩并对象图;
* 更新指向被移动了位置的对象的指针。

标记-压缩算法最大的难点在于如何选择所使用的压缩算法,如果压缩算法选择不好,将会导致极大的程序性能问题,如导致Cache命中率低等。一般来说,根据压缩后对象的位置不同,压缩算法可以分为以下三种:

任意:移动对象时不考虑它们原来的次序,也不考虑它们之间是否有互相引用的关系。

线性:尽可能的将原来的对象和它所指向的对象放在相邻位置上,这样可以达到更好的空间局部性。

滑动:将对象“滑动”到堆的一端,把存活对象之间的自由单元“挤出去”,从而维持了分配时的原始次序。

4. 节点拷贝算法(解决内部碎片)

    节点拷贝算法是把整个堆分成两个半区(From,To), GC的过程其实就是把存活对象从一个半区From拷贝到另外一个半区To的过程,而在下一次回收时,两个半区再互换角色。在移动结束后,再更新对象的指针引用

参考 http://blog.csdn.net/sinat_36246371/article/details/53002209

什么时候触发垃圾回收

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

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

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

转自http://www.cnblogs.com/dolphinX/p/3348468.html

 

posted @ 2017-04-21 14:18  维鹏_wrx  阅读(146)  评论(0编辑  收藏  举报