59

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

 6 years ago
source link: http://obkoro1.com/2018/07/08/JS高程中的垃圾回收机制与常见内存泄露的解决方法/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

zIBbIze.jpg!web

起因是因为想了解闭包的内存泄露机制,然后想起《js高级程序设计》中有关于垃圾回收机制的解析,之前没有很懂,过一年回头再看就懂了,写篇博客与大家分享一下。如果喜欢的话可以点波赞/关注,支持一下。

个人博客了解一下:obkoro1.com

内存的生命周期:

  1. 分配你所需要的内存:

由于字符串、对象等没有固定的大小,js程序在每次创建字符串、对象的时候,程序都会 分配内存来存储那个实体

  1. 使用分配到的内存做点什么。

  2. 不需要时将其释放回归:

在不需要字符串、对象的时候,需要释放其所占用的内存,否则将会消耗完系统中所有可用的内存,造成系统崩溃,这就是 垃圾回收机制所存在的意义

所谓的内存泄漏指的是:由于疏忽或错误造成程序未能释放那些已经不再使用的内存,造成内存的浪费。

垃圾回收机制:

在C和C++之类的语言中,需要手动来管理内存的,这也是造成许多不必要问题的根源。幸运的是,在编写js的过程中,内存的分配以及内存的回收完全实现了自动管理,我们不用操心这种事情。

垃圾收集机制的原理:

垃圾收集器会按照固定的时间间隔, 周期性的找出不再继续使用的变量,然后释放其占用的内存

什么叫不再继续使用的变量?

不再使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程中存在,当函数运行结束,没有其他引用(闭包),那么该变量会被标记回收。

全局变量的生命周期直至浏览器卸载页面才会结束,也就是说 全局变量不会被当成垃圾回收

标记清除:当前采用的垃圾收集策略

工作原理:

当变量进入环境时(例如在函数中声明一个变量),将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。

工作流程:

  1. 垃圾收集器会在运行的时候会给存储在内存中的 所有变量都加上标记
  2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。
  3. 那些还存在标记的变量被视为准备删除的变量。
  4. 最后垃圾收集器会执行最后一步内存清除的工作,销毁那些带标记的值并回收它们所占用的内存空间。

到2008年为止,IE、Chorme、Fireofx、Safari、Opera 都使用标记清除式的垃圾收集策略 ,只不过垃圾收集的时间间隔互有不同。

引用计数略:被废弃的垃圾收集策

循环引用:跟踪记录每个值被引用的技术

在老版本的浏览器中(对,又是IE),IE9以下BOM和DOM对象就是使用C++以COM对象的形式实现的。

COM的垃圾收集机制采用的就是引用计数策略,这种机制在出现循环引用的时候永远都释放不掉内存。

var element = document.getElementById('something');
var myObject = new Object();
myObject.element = element; // element属性指向dom
element.someThing = myObject; // someThing回指myObject 出现循环引用(两个对象一直互相包含 一直存在计数)。

解决方式是,当我们不使用它们的时候,手动切断链接:

myObject.element = null; 
element.someThing = null;

淘汰:

IE9把BOM和DOM对象转为了真正的js对象,避免了使用这种垃圾收集策略,消除了IE9以下常见的内存泄漏的主要原因。

IE7以下有一个声明狼藉的性能问题,大家了解一下:

  1. 256个变量,4096个对象(或数组)字面或者64KB的字符串,达到任何一个临界值会触发垃圾收集器运行。
  2. 如果一个js脚本的生命周期一直保有那么多变量,垃圾收集器会一直频繁的运行,引发严重的性能问题。

IE7已修复这个问题。

哪些情况会引起内存泄漏?

虽然有垃圾回收机制,但我们在编写代码的时候,有些情况还是会造成内存泄漏,了解这些情况,并在编写程序的时候,注意避免,我们的程序会更具健壮性。

意外的全局变量:

上文我们提到了 全局变量不会被当成垃圾回收 ,我们在编码中有时会出现下面这种情况:

function foo() {
 this.bar2 = '默认绑定this指向全局' // 全局变量=> window.bar2
  bar = '全局变量'; // 没有声明变量 实际上是全局变量=>window.bar
}
foo();

当我们使用 默认绑定 ,this会指向全局, this.something 也会创建一个全局变量,这一点可能很多人没有注意到。

解决方法:在函数内使用严格模式or细心一点

function foo() {
  "use strict"; 
  this.bar2 = "严格模式下this指向undefined"; 
  bar = "报错";
}
foo();

当然我们也可以 手动释放全局变量的内存

window.bar = undefined
delete window.bar2

被遗忘的定时器和回调函数

不需要 setInterval 或者 setTimeout 时, 定时器没有被clear ,定时器的 回调函数以及内部依赖的变量都不能被回收 ,造成内存泄漏。

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        node.innerHTML = JSON.stringify(someResource));
        // 定时器也没有清除
    }
    // node、someResource 存储了大量数据 无法回收
}, 1000);

解决方法: 在定时器完成工作的时候,手动清除定时器。

闭包:

闭包可以维持函数内局部变量,使其得不到释放,造成内存泄漏。

function bindEvent() {
  var obj = document.createElement("XXX");
  var unused = function () {
      console.log(obj,'闭包内引用obj obj不会被释放');
  };
  // obj = null;
}

解决方法:手动解除引用, obj = null

循环引用问题

就是IE9以下的循环引用问题,上文讲过了。

没有清理DOM元素引用:

var refA = document.getElementById('refA');
document.body.removeChild(refA); // dom删除了
console.log(refA, "refA");  // 但是还存在引用 能console出整个div 没有被回收

不信的话,可以看下这个 dom

解决办法:refA = null;

console保存大量数据在内存中。

过多的console,比如定时器的console会导致浏览器卡死。

解决:合理利用console,线上项目尽量少的使用console,当然如果你要发招聘,除外。

如何避免内存泄漏:

记住一个原则:不用的东西,及时归还,毕竟你是’借的’嘛。

  1. 减少不必要的全局变量,使用严格模式避免意外创建全局变量。
  2. 在你使用完数据后,及时解除引用(闭包中的变量,dom引用,定时器清除)。
  3. 组织好你的逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。

关于内存泄漏:

  1. 即使是1byte的内存,也叫内存泄漏,并不一定是导致浏览器崩溃、卡顿才能叫做内存泄漏。
  2. 一般是堆区内存泄漏,栈区不会泄漏。

基本类型的值存在内存中,被保存在栈内存中,引用类型的值是对象,保存在堆内存中。所以 对象、数组之类的,才会发生内存泄漏

  1. 使用chorme监控内存泄漏,可以看一下这篇 文章

结语

了解了内存泄漏的原因以及出现的情况,那么我们在编码过程中只要多加注意,就不会发生非常严重的内存泄漏问题。

PS:目前离职中,有坑位可以介绍一下,base:上海2号线淞虹路。

希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励。

个人blog and 掘金个人主页 ,如需转载,请放上原文链接并署名。码字不易, 感谢 支持!

如果喜欢本文的话,欢迎关注我的订阅号,漫漫技术路,期待未来共同学习成长。

EVvU7zN.jpg!web

以上2018.7.7

参考资料:

JS高程4.3垃圾收集

4类 JavaScript 内存泄漏及如何避免

JavaScript内存泄露及解决方案详解

4类 JavaScript 内存泄漏及如何避免


Recommend

  • 59

  • 42
    • studygolang.com 6 years ago
    • Cache

    Golang的垃圾回收(GC)机制

    转载自 https://blog.csdn.net/liangzhiyang/article/details/52670021 请先阅读

  • 54
    • blog.luhuancheng.com 6 years ago
    • Cache

    Java虚拟机的垃圾回收机制

    在Java语言中,程序员通过关键字new来创建对象,当使用完创建出来的对象后,程序员无需手动释放内存。这部分释放内存的工作就叫做垃圾收集,由JVM来承担。 哪些内存需要回收 JVM将内存划分为几个区域:程序计数器、J...

  • 59

    浏览器垃圾回收机制与 Vue 项目内存泄漏场景分析 浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。 ...

  • 33
    • 微信 mp.weixin.qq.com 5 years ago
    • Cache

    PHP 的垃圾回收机制

    在平时php-fpm的时候,可能很少人注意php的变量回收,但是到swoole常驻内存开发后,就不得不重视这个了,因为在常驻内存下,如果不了解变量回收机制,...

  • 23

    作者:张首富时间:2020-07-19wx:y18163201我们在之前的文章里面讲过Docker垃圾回收机制里面简单的介绍了下docker*prune命令,今天我们来详细的解读下最后一个dockersystemprune指令,解读这个命令之前我们先来了解下dockersystemdfdockersystemdf此参数要在client...

  • 12
    • segmentfault.com 4 years ago
    • Cache

    JavaScript引擎V8中的垃圾回收机制

    目录 认识V8 V8垃圾回收策略 V8常用的GC算法 V8的分代回收 V8的内存分配 新生代对象回收 主要使用算法 ...

  • 9
    • blog.poetries.top 4 years ago
    • Cache

    JS内存泄漏与垃圾回收机制

    由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以...

  • 5

    常见的垃圾回收器你知道有哪些吗? 发表于...

  • 6
    • segmentfault.com 3 years ago
    • Cache

    JVM常见的垃圾回收算法

    1、Mark-Sweep  标记-清除算法算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。标记过程就是对象是否属于垃圾的...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK