< 今日份知识点:谈谈内存泄漏 及 在 Javascript 中 针对内存泄漏的垃圾回收机制 >


💬 前言

说起 “ 内存泄漏 ”,科班出身的卷王们应该第一时间会想到 C语言的指针,对内存的分配 或者 其他操作。程序需要运行,必然会占用内存,就好比我们在电脑上运行程序,就必须向运行的软件程序提供内存,它才能运行。

程序运行会生成对应的服务进程,对于一些持续的服务进程,它会持续的占用内存,但是当服务运行一个来回时,如果上次运行申请的内存,没有得到及时的释放,就会导致 “ 内存泄漏 ”通俗点说,就是指被遗漏的内存持续占用堆积

这样的结果显而易见,会造成服务性能降低,严重会导致服务卡顿等现象。所以这里就需要我们去了解 “ 内存泄漏 ” 的原理,避免出现内存持续占用的情况! 接下来,就由小温带大家去了解 “ 内存泄漏 ” 相关的知识点吧!


👉 一、“ 内存泄漏 ”简述

内存泄漏(Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存

并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费

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

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

在这里插入图片描述
C语言 为例,由于 C语言 是 手动管理内存,如果程序设计不当,是非常容易导致 “ 内存泄漏 ”

// 申请内存
char * buffer;
buffer = (char*) malloc(42);

// Do something with buffer
...

// 使用free方法释放内存
free(buffer);

上面案例中,在C语言中,使用malloc方法用来申请内存,使用完毕之后,必须自己用 free 方法释放内存。

这种手动管理内存的方式非常麻烦,在申请内存的变量多时,操作累赘重复。所以为了提高编写效率,现在大多数语言提供自动内存管理,减轻程序员的负担,这被称为 " 垃圾回收机制 "

👉 二、 垃圾回收机制

Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存,自动进行释放等操作!

> 原理

垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。

那么,可能会有卷王疑惑了,垃圾收集器怎么知道我哪些变量还要使用,哪些变量在指定位置不需要使用了呢? 这就引出了 “ 垃圾回收机制 ” 的实现方式了,内容如下:

通常情况下有两种实现方式:

  • 标记清除 : 通过判断变量进出执行环境,标记变量在指定位置所处的状态,若变量已离开执行所处的环境,此变量将等待 “ 垃圾回收

  • 引用计数 : 通过判断变量被引用的次数,判断此变量是否任需使用。如果引用次数为 “ 0 ”,那么此变量占用的内存将会被回收!

> 实现方式(详)

① 标记清除

判断原理 : 当变量进入执行环境时,就标记这个变量为“进入环境“。进入环境的变量所占用的内存就不能被释放,当变量离开环境时,则将其标记为“离开环境

垃圾回收程序运行的时候,会标记内存中存储的所有变量。然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。

垃圾回收机制 - 标记清除

在此之后再被加上标记的变量就是待删除的了,原因是任何在上下文中的变量都访问不到它们了

随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。

优点:

  • 实现简单,标记情况无非是打与不打的两种情况,通过二进制(0和1)就可以为其标记。
  • 能够回收循环引用的对象
  • v8引擎使用最多的算法。

缺点:

在清除垃圾之后,剩余对象的内存位置是不变的,就会导致空闲内存空间不连续。这样就出现了内存碎片,并且由于剩余空间不是整块,就需要考虑内存分配的问题。

举个例子:

var m = 0,n = 19  		// 把 m,n,add() 标记为进入环境。
add(m, n) 				// 把 a, b, c标记为进入环境,当此方法体运行完,代表内部的局部变量离开环境。
console.log(n) 			// a,b,c标记为离开环境,等待垃圾回收。

function add(a, b) {
  a++
  let c = a + b
  return c
}

② 引用计数

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

tips: 如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。

const arr = [1, 2, 3, 4];  // arr 占用的内存泄漏
console.log('hello world');

上面代码中,数组arr, 存储的 [1, 2, 3, 4] 是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它还是会持续占用内存。

如果需要这块内存被垃圾回收机制释放,只需要设置如下:

arr = null

通过设置arr为null,就解除了对数组[1,2,3,4]的引用,引用次数变为 0,就会立刻被垃圾回收了。

优点:

  • 引用计数为零时,发现垃圾立即回收
  • 最大限度减少程序暂停

缺点:

  • 无法回收循环引用的对象
  • 空间开销比较大

👉 三、常见的几种内存泄漏的情景

①在某个局部代码块中,出现全局变量操作

function foo(arg) {
    bar = "this is a hidden global variable";
}

② 全局变量可能由 this 创建

function foo() {
    this.variable = "potential accidental global";
}
// foo 调用自己,this 指向了全局对象(window)
foo();

tips:上述使用严格模式,可以避免意外的全局变量

③ 定时器也常会造成内存泄露

var someResource = getData();
setInterval(() => {
    var node = document.getElementById('Node');
    if(node) {
        // 处理 node 和 someResource
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);

如果id为Node的元素从DOM中移除,该定时器仍会存在,同时,因为回调函数中包含对someResource的引用,定时器外面的someResource也不会被释放

④ 包括之前所说的闭包,维持函数内局部变量,不能及时释放,一样会造成内存泄漏

function bindEvent() {
  var obj = document.createElement('XXX');
  var unused = function () {
    console.log(obj, '闭包内引用obj obj不会被释放');
  };
  obj = null; // 解决方法
}

⑤ 没有清理对DOM元素的引用同样造成内存泄露

const refA = document.getElementById('refA');
document.body.removeChild(refA); // dom删除了
console.log(refA, 'refA'); // 但是还存在引用能console出整个div 没有被回收
refA = null;
console.log(refA, 'refA'); // 解除引用

⑥ 使用事件监听addEventListener监听的时候,在不监听的情况下使用removeEventListener取消对事件监听

以上为日常开发中,比较常见的内存泄漏场景,需要在平时开发中加以留意!

> 小结

虽然JavaScript提供了一套 垃圾回收机制 , 但是并不代表不用关注内存泄露。那些很占空间的值,一旦不再用到,需要及时检查是否还存在对它们的引用。如果是的话,就必须手动解除引用,这是日常开发中,需要注意的JavaScript书写规范!

今天的内容就此结束,如果觉得对你有帮助的话,不妨点点赞,支持一下小温呀!


📃 参考文献

往期内容 💨

🔥 < 今日份知识点:浅述对 “ Vue 插槽 (slot) ” 的理解 以及 插槽的应用场景 >

🔥 <恢复更新进度ing:今天浅聊一下前端CSS样式 及 书写的规范 >

🔥 < 每日份知识快餐:axios是什么?如何在Vue中 封装 axios ? >

🔥 < 今日份知识点:web常见的攻击方式(网络攻击)有哪些?如何预防?如何防御呢 ? >

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术宅小温

你小小的鼓励,是我搬砖的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值