JS的内存泄露及处理方式

概念

    应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收,就叫做内存泄漏(memory leak)

内存的生命周期

  1. 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
  2. 内存使用:即读写内存,也就是使用变量、函数等
  3. 内存回收:使用完毕,由垃圾回收机制自动回收不再使用的内存
var a = 20        // 在内存中给数值变量分配空间   (JavaScript在定义变量时就完成了内存分配)
console.log(a + 100)   // 使用内存
var a = null      // 使用完毕之后,释放内存空间

垃圾回收机制

对垃圾回收算法来说,核心思想就是如何判断内存已经不再使用了

方法一 :引用计数法(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,内存就可以释放出来了

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

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

常见的内存泄漏情况

  1. 全局变量引起的内存泄漏
function fn() {
	num = 123   // 未声明的隐式变量等同于  window.num (全局变量)
}

// 调用完了函数以后,变量仍然存在,导致泄漏
function Fn() {
	this.num = 123    // this 指向了全局对象(window)
}

// 没有对象调用Fn,也没有给它绑定this, 因此this指向window 

注意: 如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义

  1. 闭包引起的内存泄露
function outFn() {
	var num = 123    // 被闭包所引用的变量,不会被回收
	return function() {
		console.log(num)
	}
}
// 调用完了函数以后,变量仍然存在,导致泄漏
  1. 被遗忘的定时器或回调
var serverData = loadData()
setInterval(function() {
	var renderer = document.getElementById('renderer')
	if(renderer) {
		renderer.innerHTML = JSON.stringify(serverData)
	}
}, 5000) 
  1. dom清空或删除时,事件未清除导致的内存泄漏
<div id="container">  
</div>

$('#container').bind('click', function(){
    console.log('click');
}).remove();

$('#container').bind('click', function(){
    console.log('click');
}).off('click').remove();

//把事件清除了,即可从内存中移除
  1. 反复重写同一个属性会造成内存大量占用(但关闭IE后内存会被释放)
for(i = 0; i < 5000; i++) { 
	hostElement.text = "asdfasdfasdf"; 
} 
// 这种方式相当于定义了5000个属性

for(var i = 0; i < 5000; i++) { 
	hostElement.text = "asdfasdfasdf"; 
} 
// 把 i 声明一下

内存泄露如何避免

  1. 注意程序逻辑,避免“死循环”之类的
  2. 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收
  3. 避免创建过多的对象 原则:不用了的东西要及时归还

内存泄漏的识别方法

一、浏览器

  1. 打开开发者工具,选择 Timeline 面板
  2. 在顶部的Capture字段里面勾选 Memory
  3. 点击左上角的录制按钮。
  4. 在页面上进行各种操作,模拟用户的使用情况。
  5. 一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况。

二、命令行

    命令行可以使用 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字段为准。

猜你喜欢

转载自blog.csdn.net/fu983531588/article/details/89465707