JS基础-垃圾回收与内存泄露

垃圾回收

  • 对于在JavaScript中的字符串,对象,数组是没有固定大小的,只有当对他们进行动态分配存储时,解释器就会分配内存来存储这些数据,当JavaScript的解释器消耗完系统中所有可用的内存时,就会造成系统崩溃。
  • 内存泄漏,在某些情况下,不再使用到的变量所占用内存没有及时释放,导致程序运行中,内存越占越大,极端情况下可以导致系统崩溃,服务器宕机。
  • JavaScript有自己的一套垃圾回收机制,JavaScript的解释器可以检测到什么时候程序不再使用这个对象了(数据),就会把它所占用的内存释放掉。
  • 针对JavaScript的来及回收机制有以下两种方法(常用):标记清除,引用计数
  • 标记清除

v8 的垃圾回收机制基于分代回收机制,这个机制又基于世代假说,这个假说有两个特点,一是新生的对象容易早死,另一个是不死的对象会活得更久。基于这个假说,v8 引擎将内存分为了新生代和老生代。

  • 新创建的对象或者只经历过一次的垃圾回收的对象被称为新生代。经历过多次垃圾回收的对象被称为老生代。
  • 新生代被分为 From 和 To 两个空间,To 一般是闲置的。当 From 空间满了的时候会执行 Scavenge 算法进行垃圾回收。当我们执行垃圾回收算法的时候应用逻辑将会停止,等垃圾回收结束后再继续执行。

这个算法分为三步:

  • 首先检查 From 空间的存活对象,如果对象存活则判断对象是否满足晋升到老生代的条件,如果满足条件则晋升到老生代。如果不满足条件则移动 To 空间。
  • 如果对象不存活,则释放对象的空间。
  • 最后将 From 空间和 To 空间角色进行交换。

新生代对象晋升到老生代有两个条件:

  • 第一个是判断是对象否已经经过一次 Scavenge 回收。若经历过,则将对象从 From 空间复制到老��代中;若没有经历,则复制到 To 空间。
  • 第二个是 To 空间的内存使用占比是否超过限制。当对象从 From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。设置 25% 的原因主要是因为算法结束后,两个空间结束后会交换位置,如果 To 空间的内存太小,会影响后续的内存分配。

老生代采用了标记清除法和标记压缩法。标记清除法首先会对内存中存活的对象进行标记,标记结束后清除掉那些没有标记的对象。由于标记清除后会造成很多的内存碎片,不便于后面的内存分配。所以了解决内存碎片的问题引入了标记压缩法。

由于在进行垃圾回收的时候会暂停应用的逻辑,对于新生代方法由于内存小,每次停顿的时间不会太长,但对于老生代来说每次垃圾回收的时间长,停顿会造成很大的影响。为了解决这个问题 V8 引入了增量标记的方法,将一次停顿进行的过程分为了多步,每次执行完一小步就让运行逻辑执行一会,就这样交替运行

内存泄露

  • 意外的全局变量: 无法被回收
  • 定时器: 未被正确关闭,导致所引用的外部变量无法被释放
  • 事件监听: 没有正确销毁 (低版本浏览器可能出现)
  • 闭包
    • 第一种情况是我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
    • 第二种情况是我们设置了setInterval定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
    • 第三种情况是我们获取一个DOM元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。
    • 第四种情况是不合理的使用闭包,从而导致某些变量一直被留在内存当中。
  • dom 引用: dom 元素被删除时,内存中的引用未被正确清空
  • 控制台console.log打印的东西

可用 chrome 中的 timeline 进行内存标记,可视化查看内存的变化情况,找出异常点。



原文始发于微信公众号(消失的程序员):JS基础-垃圾回收与内存泄露

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/250945.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!