正经前端,谁懂内存管理啊

368 阅读4分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。

80%以上的前端开发,都不理解js的内存管理机制。主要原因是JS具有自动分配空间以及自动释放的回收机制。

去年女朋友去面试,面中级前端岗位,有一家面试官问JS的内存管理,直接就被问住了,回来就给我吐槽。

我寻思正经前端,谁懂内存管理啊。

网上关于内存管理的帖子多数是参考MDN关于内存管理介绍的,就是这篇:# Memory Management

当前端页面出现性能问题时,需要去关注内存问题,这也是JavaScript进阶知识必备。

所以写了这篇文章,XDM一起进步。

JavaScript内存管理

JS是高级语言,它的GC由浏览器(JavaScript引擎)完成的,所以对于开发来说,无需知道幕后发生了什么,也不需要收到控制。

JavaScript的内存管理由三部分组成。

  1. 在定义变量时,会自动分配一个可用的内存块来存储这个值,并且引用链接到这个对象。
  2. 在代码中使用这个变量。
  3. 当变量不在被使用(或不在被引用)时,堆中的内存将自动释放以防止内存泄漏。

大多数内存管理出问题就在这,如何找到被分配的内存已经不在需要了,并且被回收是问题的关键。

高级语言解释器嵌入了“垃圾回收器”,主要工作就是跟踪内存的分配和使用,当分配的内存不再被使用时,自动释放。下面介绍两种垃圾回收算法。

两种垃圾回收机制

1.引用计数垃圾回收

这是最初级的垃圾收集算法。原理是:如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。

示例:

var o = {
  a: {
    b:2
  }
};
// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 很显然,没有一个可以被垃圾收集

var o2 = o; // o2变量是第二个对“这个对象”的引用

o = 1;      // 现在,“这个对象”只有一个o2变量的引用了,“这个对象”的原始引用o已经没有

var oa = o2.a; // 引用“这个对象”的a属性
               // 现在,“这个对象”有两个引用了,一个是o2,一个是oa

o2 = "yo"; // 虽然最初的对象现在已经是零引用了,可以被垃圾回收了
           // 但是它的属性a的对象还在被oa引用,所以还不能回收

oa = null; // a属性的那个对象现在也是零引用了
           // 它可以被垃圾回收了

缺陷:循环引用,如果两个对象创建后,并且互相引用,形成了一个循环。即使没有用了,也不会被回收,因为引用计数算法考虑到他们互相都有一次引用。

示例

function f(){
  var o = {};
  var o2 = {};
  o.a = o2; // o 引用 o2
  o2.a = o; // o2 引用 o

  return "azerty";
}

f();

2.标记-清除算法

这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。

标记-清除算法假定了一个根(root)对象。垃圾回收器定期从根开始,找所有根开始引用的对象,然后找这些对象引用的对象。

拿上面的循环引用的例子,函数调用返回后,两个对象oo2都无法从全局对象出发获取,所以会被垃圾回收器回收。

从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,所以开发者不太会去关心垃圾回收机制。

如何发现解决

当内存问题出现,可能出现的现象:

  • 随着时间的推移,页面的性能会逐渐变差。 这可能是内存泄漏的表现。内存泄漏是指页面中的错误导致页面随着时间的推移逐渐使用越来越多的内存。
  • 页面的性能一直很差。 这可能是内存膨胀的症状。内存膨胀是指页面使用的内存超过了最佳页面速度所需的内存。
  • 页面的性能延迟或似乎经常暂停。 这可能是频繁垃圾收集的症状。垃圾收集是浏览器回收内存的时候。浏览器决定何时发生这种情况。在收集期间,所有脚本执行都会暂停。因此,如果浏览器大量收集垃圾,脚本执行就会暂停很多。 如何查看并修复内存问题:可以查看chrome提供的文档 # Fix memory problems