资讯专栏INFORMATION COLUMN

20170605-内存泄漏和垃圾回收

KitorinZero / 2560人阅读

摘要:引用计数另一种不太常见的垃圾回收策略是引用计数。引用计数的含义是跟踪记录每个值被引用的次数。在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,和还将会继续存在,因为他们的引用次数永远不会是。

垃圾回收的必要性

由于字符串、对象和数组没有固定大小,所以当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。 ---《JavaScript权威指南(第四版)》

JavaScript的解释器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用的内存释放掉了。例如:

var a = "before";
var b = "override a";
var a = b; //重写a

这段代码运行之后,“before”这个字符串失去了引用(之前是被a引用),系统检测到这个事实之后,就会释放该字符串的存储空间以便这些空间可以被再利用。

垃圾回收原理

最常采用的垃圾回收有两种方法:标记清除、引用计数

标记清除

当执行流入到一个函数中时,会创建该函数的执行环境,执行环境中的变量都会被标记为“进入环境”,从逻辑上讲,永远不能释放“进入执行环境”变量所占用的内存。因为只要执行流入相应的执行环境,就可能会用到这些变量。
垃圾收集器在运行的时候会给存储在内存的中的变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

引用计数

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

但是用这种方法存在问题:

function problem(){
    var objA = new Object()
    var objB = new Object()
    objA.someOtherObject = objB
    objB.someOtherObject = objA
}

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

引起内存泄漏的操作 用全局变量缓存数据

将全局变量作为缓存数据的一种方式,将之后要用到的数据都挂载到全局变量上,用完之后也不手动释放内存(因为全局变量引用的对象,垃圾回收机制不会自动回收),全局变量逐渐就积累了一些不用的对象,导致内存泄漏

var x = [];
function grow() {
  x.push(new Array(1000000).join("x"));
  /*
     使用x数组进行某些操作
  */
  setTimeout(grow, 1000);
}
grow()
没有清理的DOM元素引用
(function () {
  var nodes = "";
  var item = {
    // 为了凸显
    name: new Array(1000000).join("x")
  }
  nodes = document.getElementById("nodes")
  nodes.item = item
  nodes.parentElement.removeChild(nodes)
})()

notes变量指向的是页面中的一个元素(也是内存中的一块空间),当将 notes 对应的元素从页面中移除后,其在内存中对应的空间由于仍然由notes变量指向(引用),因此垃圾回收机制不会将这块内存空间回收,从而导致内存泄漏

 被遗忘的定时器或者回调
var someResource = getData();
setInterval(function() {
    var node = document.getElementById("Node");
    if(node) {
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);

这样的代码很常见, 如果id为Node的元素从DOM中移除, 该定时器仍会存在, 同时, 因为回调函数中包含对someResource的引用, 定时器外面的someResource也不会被释放.

闭包循环引用
var theThing = null  
var replaceThing = function () {
  var originalThing = theThing
  var unused = function () {
    if (originalThing)
      console.log("hi")
  }
  theThing = {
    longStr: new Array(1000000).join("*"),
    someMethod: function () {
      console.log(someMessage)
    }
  };
};
setInterval(replaceThing, 1000)

这种内存泄漏的分析请参考here

参考

《JavaScript权威指南》
javascript典型内存泄漏及chrome的排查方法

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83322.html

相关文章

  • JS高程中的垃圾回收机制与常见内存泄露的解决方法

    摘要:解决方式是,当我们不使用它们的时候,手动切断链接淘汰把和对象转为了真正的对象,避免了使用这种垃圾收集策略,消除了以下常见的内存泄漏的主要原因。以上参考资料高程垃圾收集类内存泄漏及如何避免内存泄露及解决方案详解类内存泄漏及如何避免 showImg(http://ww1.sinaimg.cn/large/005Y4rCogy1ft1ikzcqzqj30ka0et77a.jpg); 前言 起...

    kidsamong 评论0 收藏0
  • JavaScript中的垃圾回收内存泄漏

    摘要:所谓的内存泄漏简单来说是不再用到的内存,没有及时释放。如果一个值不再需要了,引用数却不为,垃圾回收机制无法释放这块内存,从而导致内存泄漏。 前言 程序的运行需要内存。只要程序提出要求,操作系统或者运行时就必须供给内存。所谓的内存泄漏简单来说是不再用到的内存,没有及时释放。为了更好避免内存泄漏,我们先介绍Javascript垃圾回收机制。 在C与C++等语言中,开发人员可以直接控制内存的...

    microelec 评论0 收藏0
  • JavaScript中的垃圾回收内存泄漏

    摘要:所谓的内存泄漏简单来说是不再用到的内存,没有及时释放。如果一个值不再需要了,引用数却不为,垃圾回收机制无法释放这块内存,从而导致内存泄漏。 前言 程序的运行需要内存。只要程序提出要求,操作系统或者运行时就必须供给内存。所谓的内存泄漏简单来说是不再用到的内存,没有及时释放。为了更好避免内存泄漏,我们先介绍Javascript垃圾回收机制。 在C与C++等语言中,开发人员可以直接控制内存的...

    hover_lew 评论0 收藏0
  • JS中的垃圾回收内存泄漏

    摘要:介绍浏览器的具有自动垃圾回收机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。中的内存泄漏问题程序的内存溢出后,会使某一段函数体永远失效取决于当时的代码运行到哪一个函数,通常表现为程序突然卡死或程序出现异常。 showImg(https://segmentfault.com/img/remote/1460000018932880?w=4400&h=3080); 1. 介绍 浏...

    xiaolinbang 评论0 收藏0
  • Node.js内存管理V8垃圾回收机制

    摘要:垃圾回收内存管理实践先通过一个来看看在中进行垃圾回收的过程是怎样的内存泄漏识别在环境里提供了方法用来查看当前进程内存使用情况,单位为字节中保存的进程占用的内存部分,包括代码本身栈堆。 showImg(https://segmentfault.com/img/remote/1460000019894672?w=640&h=426);作者 | 五月君Node.js 技术栈 | https:...

    JowayYoung 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<