来自 Web前端 2020-05-07 05:42 的文章
当前位置: 网上澳门金莎娱乐 > Web前端 > 正文

Js内存泄露

每个页面上的 DOM 都是占用内存的,假设有一个页面 A 元素,我们获取到了 A 元素 DOM 对象,然后赋值到了一个变量(内存指向是一样的),然后移除了页面的 A 元素,如果这个变量由于其他原因没有被回收,那么就存在内存泄漏,如下面的例子:

大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到不再需要使用的变量。

被遗忘的事件监听器

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

// 给数值变量分配内存let number = 123; // 给字符串分配内存const string = "xianshannan"; // 给对象及其包含的值分配内存const object = { a: 1, b: null}; // 给数组及其包含的值分配内存(就像对象一样)const array = [1, null, "abra"]; // 给函数(可调用的对象)分配内存function func(a){ return a;} 

常见的内存泄露案例

内存泄漏简单理解:无用的内存还在占用,得不到释放和归还。比较严重时,无用的内存会持续递增,从而导致整个系统卡顿,甚至崩溃。

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

用户一般不会在一个 Web 页面停留比较久,即使有一点内存泄漏,重载页面内存也会跟着释放。而且浏览器也有自动回收内存的机制,所以我们前端其实并没有像 C、C++ 这类语言一样,特别关注内存泄漏的问题。

JS 环境中分配的内存有如下声明周期:1.内存分配:当我们申明变量、函数、对象的时候,系统会自动为他们分配内存2.内存使用:即读写内存,也就是使用变量、函数等3.内存回收:使用完毕,由垃圾回收机制自动回收不再使用的内存

被遗忘的计时器

内存生命周期

时间: 2019-08-30阅读: 160标签: 内存

var s = "azerty";var s2 = s.substr; // s2 是一个新的字符串// 因为字符串是不变量,// JavaScript 可能决定不分配内存,// 只是存储了 [0-3] 的范围。var a = ["ouais ouais", "nan nan"];var a2 = ["generation", "nan nan"];var a3 = a.concat; // 新数组有四个元素,是 a 连接 a2 的结果

根据上面的内存自动分配例子,我们继续内存使用的例子:

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

其中 JavaScript 语言不需要程序员手动分配内存,绝大部分情况下也不需要手动释放内存,对 JavaScript 程序员来说通常就是使用内存(即使用变量、函数、对象等)。

如何避免内存泄漏

JavaScript 内存泄漏的一些场景

此时变量 div 有事件处理函数的引用,同时事件处理函数也有div的引用!。

// “这个对象”分配给 a 变量var a = { a: 1, b: 2,}// b 引用“这个对象”var b = a; // 现在,“这个对象”的原始引用 a 被 b 替换了a = 1;

因为自动垃圾回收机制的存在,开发人员可以不关心也不注意内存释放的有关问题,但对无用内存的释放这件事是客观存在的。不幸的是,即使不考虑垃圾回收对性能的影响,目前最新的垃圾回收算法,也无法智能回收所有的极端情况。

原文:

垃圾回收算法主要依赖于引用的概念。

template div/div/templatescriptexport default { mounted() { window.addEventListener('resize', () = { // 这里做一些操作 }) },}/script

来看一个循环引用的例子:

上一步确认是内存泄漏问题后,我们继续利用谷歌开发者工具进行问题查找。

前言

这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。

定时器函数中的依赖也无法回收。在这个案例中的 serverData 也无法被回收。

上面的组件销毁的时候,resize 事件还是在监听中,里面涉及到的内存都是没法回收的(浏览器会认为这是必须的内存,不是垃圾内存),需要在组件销毁的时候移除相关的事件,如下:

上述案例中,即使我们对于 image 元素进行了移除,但是仍然有对 image 元素的引用,依然无法对齐进行内存回收。

严格来说,这样是有内存泄漏的,name变量是被closure返回的函数调用了,但是返回的函数没被使用,这个场景下name就属于垃圾内存,但是它还是占用了内存,也不可被回收。

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

第二步:查找内存泄漏出现的位置

在这里,“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域。

// 写入内存number = 234;// 读取 number 和 func 的内存,写入 func 参数内存func(number);

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

上面有没有内存泄漏?

标记清除算法

!DOCTYPE htmlhtml head meta charset="utf-8" / /head body div  button 运行/button button 停止/button /div script const arr = [] for (let i = 0; i  200000; i++) { arr.push(i) } let newArr = [] function run() { newArr = newArr.concat(arr) } let clearRun document.querySelector('#run').onclick = function() { clearRun = setInterval(() = { run() }, 1000) } document.querySelector('#stop').onclick = function() { clearInterval(clearRun) } /script /body/html

使用值的过程实际上是对分配内存进行读取与写入的操作。

首先看下这个代码:

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

分配期

这个范例的关键在于,闭包之间是共享作用域的,尽管 unused 可能一直没有被调用,但是 someMethod 可能会被调用,就会导致无法对其内存进行回收。

无用的计时器忘记清理是新手最容易犯的错误之一。

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

从内存记录中,发现 array 对象占用最大,展开后发现,第一个object elements占用最大,选择这个object elements后可以在下面看到newArr变量,然后点击test:23,只要是高亮下划线的地方都可以进去看看。 (测试页面时 test.html),可以跳转到newArr附近。

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

本人测试的谷歌版本为:版本 76.0.3809.100(正式版本) (64 位)

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

然后第二步的主要目的来了,记录 JavaScript 堆内存才是内存录制的主要目的,我们可以看到那个堆占用了内存更高。

如果没有其他对象指向它了,说明该对象已经不再需了。

意外的全局变量

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

// 假设这里是全局变量// b 被标记进入环境var b = 2;function test() { var a = 1; // 函数执行时,a 被标记进入环境 return a + b;}// 函数执行结束,a 被标记离开环境,被回收// 但是 b 就没有被标记离开环境test();

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

我们可以使用内存走势图判断当前页面是否有内存泄漏。经过测试上面的代码20000个数组项改为20个数组项,内存走势也一样能看出来。

JS 的内存回收

在刚才的录制中选择 Snapshot 3 ,然后按照Shallow Size进行逆序排序(不了解的可以看内存术语),如下:

var elements = { image: document.getElementById};function doStuff() { elements.image.src = 'http://example.com/image_name.png';}function removeImage() { document.body.removeChild(document.getElementById; // 这个时候我们对于 #image 仍然有一个引用, Image 元素, 仍然无法被内存回收.}

内存分配

如果两个对象相互引用,尽管他们已不再使用,垃圾回收不会进行回收,导致内存泄露。

JavaScript 内存管理机制和内存的生命周期是一一对应的。首先需要分配内存,然后使用内存,最后释放内存

闭包

JavaScript 定义变量就会自动分配内存的。我们只需了解 JavaScript 的内存是自动分配的就足够了

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

当变量进入执行环境时标记为“进入环境”,当变量离开执行环境时则标记为“离开环境”,被标记为“进入环境”的变量是不能被回收的,因为它们正在被使用,而标记为“离开环境”的变量则可以被回收

例如,一个Javascript对象具有对它原型的引用。

内存也是有生命周期的,不管什么程序语言,一般可以按顺序分为三个周期:

JS 的内存使用

闭包

有些方法分配新变量或者新对象:

这里不进行详细的开发者工具使用说明,详细看谷歌开发者工具,不过谷歌浏览器是不断迭代更新的,有些文档落后了,界面长得不一样。

如果后续 renderer 元素被移除,整个定时器实际上没有任何作用。

b =1;

但如果你没有回收定时器,整个定时器依然有效, 不但定时器无法被内存回收,

template div/div/templatescriptexport default { mounted() { this.resizeEventCallback = () = { // 这里做一些操作 } window.addEventListener('resize', this.resizeEventCallback) }, beforeDestroy() { window.removeEventListener('resize', this.resizeEventCallback) },}/script

引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。

这里针对下面例子进行一步一步的排查和找到问题出现在哪里:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

分配你所需要的内存

引用

这样引用的"这个对象"的内存才会被回收。

记住一个原则:不用的东西,及时归还。1.减少不必要的全局变量,使用严格模式避免意外创建全局变量。2.在你使用完数据后,及时解除引用。3.组织好你的逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。

// 在全局作用域下定义function count(number) { // basicCount 相当于 window.basicCount = 2; basicCount = 2; return basicCount + number;}

上面这种JS写法再普通不过了,创建一个DOM元素并绑定一个点击事件。

上面的组件销毁的时候,setInterval还是在运行的,里面涉及到的内存都是没法回收的(浏览器会认为这是必须的内存,不是垃圾内存),需要在组件销毁的时候清除计时器,如下:

为了不让程序员费心分配内存,JavaScript 在定义变量时就完成了内存分配。

上面的例子 button 元素 虽然在页面上移除了,但是内存指向换为了this.elements.button,内存占用还是存在的。所以上面的代码还需要这样写:this.elements.button = null,手动释放这个内存。

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

内存泄漏时,内存一般都是会周期性的增长,我们可以借助谷歌浏览器的开发者工具进行判别。

举个例子: 如果我们引用了一个表格中的td元素,一旦在 Dom 中删除了整个表格,我们直观的觉得内存回收应该回收除了被引用的 td 外的其他元素。

如果内存不需要时,没有经过生命周期的释放期,那么就存在内存泄漏

在 JS 开发中,我们会经常用到闭包,一个内部函数,有权访问包含其的外部函数中的变量。

当前执行环境中,内存还没有被回收的,只有加上下面这个才会被回收:

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

闭包是经常使用的,闭包能给我们带来很多便利。

一个循序引用出现了,按上面所讲的算法,该部分内存无可避免的泄露了。

什么是内存泄漏?在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

当这段代码被反复执行时,内存会持续增长。

本文由网上澳门金莎娱乐发布于Web前端,转载请注明出处:Js内存泄露

关键词: