JS 中内存泄漏的⼏种情况

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

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

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

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

⼤多数语⾔提供⾃动内存管理,减轻程序员的负担,这被称为"垃圾回收机制" 

垃圾回收机制
Javascript 具有⾃动垃圾回收机制( GC Garbage Collecation ),也就是说,执⾏环境会负责管理代码
执⾏过程中使⽤的内存
原理:垃圾收集器会定期(周期性)找出那些不在继续使⽤的变量,然后释放其内存
通常情况下有两种实现⽅式:
  • 标记清除
  • 引⽤计数
标记清除
JavaScript 最常⽤的垃圾收回机制
当变量进⼊执⾏环境是,就标记这个变量为 进⼊环境 。进⼊环境的变量所占⽤的内存就不能释放,当变量离开环境时,则将其标记为“ 离开环境
垃圾回收程序运⾏的时候,会标记内存中存储的所有变量。然后,它会将所有在上下⽂中的变量,以及 被在上下⽂中的变量引⽤的变量的标记去掉
在此之后再被加上标记的变量就是待删除的了,原因是任何在上下⽂中的变量都访问不到它们了
随后垃圾回收程序做⼀次内存清理,销毁带标记的所有值并收回它们的内存
举个例⼦:
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++
 var c = a + b
 return c }
引⽤计数
语⾔引擎有⼀张 " 引⽤表 " ,保存了内存⾥⾯所有的资源(通常是各种值)的引⽤次数。如果⼀个值的引 ⽤次数是 0 ,就表示这个值不再⽤到了,因此可以将这块内存释放
如果⼀个值不再需要了,引⽤数却不为 0 ,垃圾回收机制⽆法释放这块内存,从⽽导致内存泄漏
const arr = [1, 2, 3, 4];
console.log('hello world');
代码中,数组 [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();
上述使⽤严格模式,可以避免意外的全局变量
定时器也常会造成内存泄露
var someResource = getData();
setInterval(function() {
 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 取消对事件监听
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值