小时电话
17620848827

公司动态

建站经验 JS 内存泄露问题

2020-05-12 点击数:4903

JavaScript 内存泄露问题

内存泄露是每个开发者最终都不得不面对的问题。即便使用自动内存管理的语言,你还是会碰到一些内存泄漏的情况。内存泄露会导致一系列问题,比如:运行缓慢,崩溃,高延迟,甚至一些与其他应用相关的问题。

什么是内存泄漏

本质上来讲,内存泄露是当一块内存不再被应用程序使用的时候,由于某种原因,这块内存没有返还给操作系统或者空闲内存池的现象。编程语言使用不同的方式来管理内存。这些方式可能会减少内存泄露的机会。然而,某一块具体的内存是否被使用实际上是一个不可判定问题(undecidable problem)。换句话说,只有开发者可以搞清楚一块内存是否应该被操作系统回收。某些编程语言提供了帮助开发者来处理这件事情的特性。而其它的编程语言需要开发者明确知道内存的使用情况。维基百科上有几篇写的不错的讲述手动 自动内存管理的文章。

Javascript 的内存管理

Javascript 是那些被称作垃圾回收语言当中的一员。垃圾回收语言通过周期性地检查那些之前被分配出去的内存是否可以从应用的其他部分访问来帮助开发者管理内存。换句话说,垃圾回收语言将内存管理的问题从“什么样的内存是仍然被使用的?”简化成为“什么样的内存仍然可以从应用程序的其他部分访问?”。两者的区别是细微的,但是很重要:开发者只需要知道一块已分配的内存是否会在将来被使用,而不可访问的内存可以通过算法确定并标记以便返还给操作系统。

非垃圾回收语言通常使用其他的技术来管理内存,包括:显式内存管理,程序员显式地告诉编译器在何时不再需要某块内存;引用计数,一个计数器关联着每个内存块(当计数器的计数变为0的时候,这块内存就被操作系统回收)。这些技术都有它们的折中考虑(也就是说都有潜在的内存泄漏风险)。

Javascript 中的内存泄露

引起垃圾收集语言内存泄露的主要原因是不必要的引用。想要理解什么是不必要的引用,首先我们需要理解垃圾收集器是怎样确定一块内存能否被访问的。

Mark-and-sweep

大多数的垃圾收集器(简称 GC)使用一个叫做 mark-and-sweep 的算法。这个算法由以下的几个步骤组成:

垃圾收集器建立了一个“根节点”列表。根节点通常是那些引用被保留在代码中的全局变量。对于 Javascript 而言,“Window” 对象就是一个能作为根节点的全局变量例子。window 对象是一直都存在的(即:不是垃圾)。所有根节点都是检查过的并且被标记为活动的(即:不是垃圾)。所有的子节点也都被递归地检查过。每块可以从根节点访问的内存都不会被视为垃圾。 所有没有被标记为垃圾的内存现在可以被当做垃圾,而垃圾收集器也可以释放这些内存并将它们返还给操作系统。现代垃圾收集器使用不同的方式来改进这些算法,但是它们都有相同的本质:可以访问的内存块被标记为非垃圾而其余的就被视为垃圾。

不必要的引用就是那些程序员知道这块内存已经没用了,但是出于某种原因这块内存依然存在于活跃的根节点发出的节点树中。在 Javascript 的环境中,不必要的引用是某些不再被使用的代码中的变量。这些变量指向了一块本来可以被释放的内存。一些人认为这是程序员的失误。

所以想要理解什么是 Javascript 中最常见的内存泄露,我们需要知道在什么情况下会出现不必要的引用。