您的位置:首页 > Web前端 > JavaScript

JavaScript中常见的内存泄露问题

2020-04-22 02:24 1361 查看

JavaScript中常见的内存泄露问题

在此记录一下内存泄漏问题的发生
概念:
应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收,就叫做内存泄漏(memory leak)

内存的生命周期

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

垃圾回收机制

对垃圾回收算法来说,核心思想就是如何判断内存已经不再使用了
分为两个方法:标记清除法和引用计数法

方法一 :标记清除法(mark-and-sweep),有三个步骤 :

1.垃圾回收器生成一个根列表。根通常是将引用保存在代码中的全局变量。在JavaScript中,window对象是一个可以作为根的全局变量。
2.所有的根都被检查和标记成活跃的(不是垃圾),所有的子变量也被递归检查。所有可能从根元素到达的都不被认为是垃圾。
3.所以没有被标记成活跃的内存都被认为是垃圾。垃圾回收器就可以释放内存并且把内存还给操作系统。

当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

function test(){
var a = 10 ;       // 被标记 ,进入环境
var b = 20 ;       // 被标记 ,进入环境
}
test();            // 执行完毕 之后 a、b又被标离开环境,被回收。

方法一 :引用计数法(reference counting):

语言引擎有一张"引用表",保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放(被回收)。

const arr = [1, 2, 3, 4]
// 数组[1, 2, 3, 4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为 1
// 尽管后面的代码没有用到arr,它还是会持续占用内存
arr = null
// arr重置为null,就解除了对[1, 2, 3, 4]的引用,引用次数变成了0,内存就可以释放出来了

此处借鉴博客地址:
原文链接:https://blog.csdn.net/fu983531588/article/details/89465707

1.意外的全局变量

function Person(name) {
this.name = name;
}
function test() {
//使用let或var对变量进行声明
//当函数执行完毕后,p1,p2将被释放
let p1 = new Person("小明");
var p2 = new Person("小方");
//不使用任何关键字对变量进行声明
//此时该变量对被赋值到window对象上,导致它无法被释放
p3 = new Person("小红");
debugger;//在此处打断点
}
test();

在内存未执行完之前,是有三个变量p1,p2,p3
当内存执行完(断点释放后),只剩变量p3

解决方法:在JavaScript程序中添加,开启严格模式’use strict’,可以有效地避免上述问题。
如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义

2.被遗忘的定时器

function Person(name) {
this.name = name;
}
function sayHello() {
let p1 = new Person("悟空");
let p1 = new Person("八戒");
let p1 = new Person("唐僧");
console.log(p1.sayHello());
console.log(p2.sayHello());
let count = 0;
setInterval(() => {
if (count < 3) {  //"唐僧"重复三次
console.log(p3.sayHello());
count++;
}
},100);
}
sayHello();

执行完后发现p3还存在,没有被销毁;
是因为定时器setInterval执行后没有被销毁,导致p3也没有被销毁。

解决方法:在合适的时间销毁定时器

function Person(name) {
this.name = name;
}
function sayHello() {
let p1 = new Person("悟空");
let p1 = new Person("八戒");
let p1 = new Person("唐僧");
console.log(p1.sayHello());
console.log(p2.sayHello());
let count = 0;
let intervalId = setInterval(() => {  //给定时器一个名字
if (count < 3) {  //"唐僧"重复三次
console.log(p3.sayHello());
count++;
}else {
clearInterval(intervalId);  //此处销毁定时器
}
},100);
}
sayHello();

3.脱离DOM的元素引用

var A = document.getElementById('A');
document.body.removeChild(A);
//remove方法只是将A从DOM上面移除了,但并没有从内存当中移除。

解决方法:添加 A = null; 手动将变量指为空值即可。

4.闭包引起的内存泄露

window.onload = function(){
var el = document.getElementById("id");
el.onclick = function(){
alert(el.id);
}
}
解决方法为
window.onload = function(){
var el = document.getElementById("id");
var id = el.id;      //解除循环引用
el.onclick = function(){
alert(id);
}
el = null;   // 将闭包引用的外部函数中活动对象清除
}
  • 点赞
  • 收藏
  • 分享
  • 文章举报
xinyu_huo 发布了5 篇原创文章 · 获赞 0 · 访问量 66 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: