中高级前端必须了解的JS中的内存管理(推荐)(2)

标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
 简单来说,就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。
 凡是能从根部到达的对象,都是还需要使用的。
 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。

从这个概念可以看出,无法触及的对象包含了没有引用的对象这个概念(没有任何引用的对象也是无法触及的对象)。
 但反之未必成立。

工作流程:
1.垃圾收集器会在运行的时候会给存储在内存中的所有变量都加上标记。
2.从根部出发将能触及到的对象的标记清除。
3.那些还存在标记的变量被视为准备删除的变量。
4.最后垃圾收集器会执行最后一步内存清除的工作,销毁那些带标记的值并回收它们所占用的内存空间。

中高级前端必须了解的JS中的内存管理(推荐)

循环引用不再是问题了

再看之前循环引用的例子:

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

函数调用返回之后,两个循环引用的对象在垃圾收集时从全局对象出发无法再获取他们的引用。
 因此,他们将会被垃圾回收器回收。

内存泄漏

什么是内存泄漏

程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。

对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。
 否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

本质上讲,内存泄漏就是由于疏忽或错误造成程序未能释放那些已经不再使用的内存,造成内存的浪费。

内存泄漏的识别方法

经验法则是,如果连续五次垃圾回收之后,内存占用一次比一次大,就有内存泄漏。

这就要求实时查看内存的占用情况。

在 Chrome 浏览器中,我们可以这样查看内存占用情况
1.打开开发者工具,选择 Performance 面板
2.在顶部勾选 Memory
3.点击左上角的 record 按钮
4.在页面上进行各种操作,模拟用户的使用情况
5.一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况

来看一张效果图:

中高级前端必须了解的JS中的内存管理(推荐)

我们有两种方式来判定当前是否有内存泄漏:
1.多次快照后,比较每次快照中内存的占用情况,如果呈上升趋势,那么可以认为存在内存泄漏
2.某次快照后,看当前内存占用的趋势图,如果走势不平稳,呈上升趋势,那么可以认为存在内存泄漏

在服务器环境中使用 Node 提供的 process.memoryUsage 方法查看内存情况

console.log(process.memoryUsage()); // { // rss: 27709440, // heapTotal: 5685248, // heapUsed: 3449392, // external: 8772 // }

process.memoryUsage返回一个对象,包含了 Node 进程的内存占用信息。

该对象包含四个字段,单位是字节,含义如下:

rss(resident set size):所有内存占用,包括指令区和堆栈。

heapTotal:"堆"占用的内存,包括用到的和没用到的。

heapUsed:用到的堆的部分。

external: V8 引擎内部的 C++ 对象占用的内存。

判断内存泄漏,以heapUsed字段为准。

常见的内存泄露案例

意外的全局变量

function foo() { bar1 = 'some text'; // 没有声明变量 实际上是全局变量 => window.bar1 this.bar2 = 'some text' // 全局变量 => window.bar2 } foo();

在这个例子中,意外的创建了两个全局变量 bar1 和 bar2

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

在很多库中, 如果使用了观察者模式, 都会提供回调方法, 来调用一些回调函数。

要记得回收这些回调函数。举一个 setInterval的例子:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/5562ba4798942f3b86ba4c7786edc750.html