深入浅出JS的内存管理机制

其实在写这篇文章之前 , 我也想了很久 , 因为网上对这块的东西已经很多了 , 但有些读起来还是不容易让人理解 , 而且JS 中的内存管理, 我的感觉就像 JS 中的一门副科, 我们平时不会太重视, 但是一旦出问题又很棘手. 所以可以通过平时多了解一些 JS 中内存管理问题, 在写代码中通过一些习惯, 避免内存泄露的问题 。

深入浅出JS的内存管理机制

文章插图
 
内容概要
  • 内存的生命周期
  • JS的内存回收
  • 常见的内存泄露案例
内存生命周期
深入浅出JS的内存管理机制

文章插图
【深入浅出JS的内存管理机制】 
不管什么程序语言 , 内存生命周期基本是一致的:
  1. 分配你所需要的内存
  2. 使用分配到的内存(读, 写)
  3. 不需要时将其释放/归还
在 C语言中, 有专门的内存管理接口, 像malloc() 和 free(). 而在 JS 中, 没有专门的内存管理接口, 所有的内存管理都是"自动"的. JS 在创建变量时, 自动分配内存, 并在不使用的时候, 自动释放. 这种"自动"的内存回收, 造成了很多 JS 开发并不关心内存回收, 实际上, 这是错误的.
JS 中的内存回收引用
垃圾回收算法主要依赖于引用的概念. 在内存管理的环境中, 一个对象如果有访问另一个对象的权限(隐式或者显式), 叫做一个对象引用另一个对象. 例如: 一个JAVAscript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用).
引用计数垃圾收集
这是最简单的垃圾收集算法.此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”. 如果没有引用指向该对象(零引用), 对象将被垃圾回收机制回收. 示例:
let arr = [1, 2, 3, 4];arr = null; // [1,2,3,4]这时没有被引用, 会被自动回收限制: 循环引用
在下面的例子中, 两个对象对象被创建并互相引用, 就造成了循环引用. 它们被调用之后不会离开函数作用域, 所以它们已经没有用了, 可以被回收了. 然而, 引用计数算法考虑到它们互相都有至少一次引用, 所以它们不会被回收.
function f() { var o1 = {}; var o2 = {}; o1.p = o2; // o1 引用 o2 o2.p = o1; // o2 引用 o1. 这里会形成一个循环引用}f();
深入浅出JS的内存管理机制

文章插图
 
实际例子:
var div;window.onload = function(){ div = document.getElementById("myDivElement"); div.circularReference = div; div.lotsOfData = https://www.isolves.com/it/cxkf/yy/js/2019-12-06/new Array(10000).join("*");};在上面的例子里, myDivElement 这个 DOM 元素里的 circularReference 属性引用了 myDivElement, 造成了循环引用. IE 6, 7 使用引用计数方式对 DOM 对象进行垃圾回收. 该方式常常造成对象被循环引用时内存发生泄漏. 现代浏览器通过使用标记-清除内存回收算法, 来解决这一问题.
标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”.
这个算法假定设置一个叫做根root的对象(在JavaScript里 , 根是全局对象). 垃圾回收器将从根开始, 找到所有从根开始引用的对象, 然后找这些对象引用的对象, 从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象.
从2012年起, 所有现代浏览器都使用了标记-清除内存回收算法 。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进.
深入浅出JS的内存管理机制

文章插图
 
自动 GC 的问题
尽管自动 GC 很方便, 但是我们不知道GC 什么时候会进行. 这意味着如果我们在使用过程中使用了大量的内存, 而 GC 没有运行的情况下, 或者 GC 无法回收这些内存的情况下, 程序就有可能假死, 这个就需要我们在程序中手动做一些操作来触发内存回收.
什么是内存泄露?
本质上讲, 内存泄露就是不再被需要的内存, 由于某种原因, 无法被释放.
常见的内存泄露案例1. 全局变量
function foo(arg) { bar = "some text";}在 JS 中处理未被声明的变量, 上述范例中的 bar时, 会把bar, 定义到全局对象中, 在浏览器中就是 window 上. 在页面中的全局变量, 只有当页面被关闭后才会被销毁. 所以这种写法就会造成内存泄露, 当然在这个例子中泄露的只是一个简单的字符串, 但是在实际的代码中, 往往情况会更加糟糕.


推荐阅读