Skip to content

Latest commit

 

History

History
70 lines (50 loc) · 5.7 KB

垃圾回收机制.md

File metadata and controls

70 lines (50 loc) · 5.7 KB
title date tags
JavaScript篇-垃圾回收机制
2018-10-08 08:27:10 -0700

垃圾回收机制

为什么要有垃圾回收这鬼玩意 ?由于字符、数组没有固定的大小所以当他们的大小已知时,才能对他们进行动态的存储分配。js在每一次创建字符串、数组、对象等时,解释器必须分配内存来存储那个实体。只要分配了内容,最终都要释放,不然解释器将会消耗完系统中所有可用的内存。导致系统崩溃(好像很牛逼的样子)

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

标记清除

  • 这是js最常用的垃圾回收方法,当变量进入执行环境时,就标记着这个变量为 “ 进入环境 ” (come on ~),从逻辑上来讲不能释放 “进入环境” 的变所占用的内存,因为只要变量进入环境,意味着可能会用到该变量。当变量离开环境则标记为 “ 离开环境 ”

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

引用计数

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

你以为这就结束了吗 ? NO !

博主之前面试,有个题目就是问如果两个对象通过各自的属性相互引用,会出什么情况呢 ? 如何解决 ?

    function problem() {
        var objA = new Object();
        var objB = new Object();

        objA.someOtherObject = objB;
        objB.anotherObject = objA;
    }

在这个例子中,objA和objB通过自身属相互引用,也就是说这两个对象的引用次数都是2。在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,objA和objB还将会继续存在,因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。

如何解决呢 ? 手切断他们的循环引用 !

    objA.someOtherObject = null
    objB.anotherObject = null

在这个例子中,objA和objB通过自身属相互引用,也就是说这两个对象的引用次数都是2。在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,objA和objB还将会继续存在,因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。

性能问题

垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很可观,那么回收工作量也是非常大的。像IE的垃圾收集器是根据内存分配量运行的,具体一点说就是256个变量,4096个对象(或数组)字面量和数组元素(slot)或者64KB的字符串,上述任何一个临界值,垃圾收集器就会运行。在IE7发布之后,JavaScript引擎的垃圾收集改变了工作方式: 触发垃圾收集的变量分配、字面量和数组元素的临界值被调整为动态修改,如果垃圾收集例程回收的内存分配量低于15%,则变量、字面量和数组元素的临界值就会加倍。也就是说,垃圾收集例程回收了85%的内存分配量,则将各种临界值重置回默认值

管理内存

分配给Web浏览器的可用内存数量通常要比分配给桌面应用程序的少,目的是: 出于安全方面的考虑,防止运行JavaScript的网页耗尽全部系统内存而导致系统奔溃。因此,确保占用最少的内存可以让页面获得更好的性能,而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据, 一旦数据不再有用,则将值设置为 null来释放这个引用,这种方式就是 接触引用

局部变量会在他们离开执行环境时自动被解除引用

  function createPerson(name) {
    var localPerson = new Object()
    localPerson.name = name
    return localPerson
  }

  var globalPerson = createPerson('彭道宽')

  // 手动解除 globalPerson 的引用
  globalPerson = null

上诉例子中,变量 globalPerson 取得了 createPerson() 函数返回的值。在createPerson() 内部,我们创建了一个对象并将其赋给局部变量localPerson, 然后又为该对象添加了一个名为 name 的属性。

最后调用这个函数时,localPerson 以函数值的形式返回并赋给全局变量 globalPerson。由于 localPerson 在 createPerson() 函数执行完毕之后,就离开了其执行环境,因此,我们不需要去为它解除引用,但是对于全局变量 globalPerson 来说,则需要我们在不使用它的时候,手动解除它的引用。

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