总结内存泄漏出现的情况和如何避免
一 定义
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
垃圾回收方式一般有两种,分别是引用计数和标记清除
引用计数:语言引擎中的引用表会标记内存资源的引用次数,当引用的计数为0代表资源没有被引用获取,则释放该资源
标记清除:当变量进入执行环境会做标记,当离开执行环境脱离当前上下文会被释放
但并不是什么时候内存都能够正确释放,以下几种情况会出现内存泄漏情况
二 出现内存泄漏的几种情况
1 全局变量定义问题
function a(){
a=10;
}
函数中未使用变量定义命令会直接定义为全局变量,定义过多会导致内存泄漏,规范变量定义,函数内部注意不要丢失变量定义命令 var
,或者let
,const
定义局部变量常量的方式
function foo() {
this.a = "a";
}
foo();
非严格模式,this 指向了全局对象(window),使用严格模式可避免
2 闭包问题
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
闭包的一大作用就是暂存变量到内存,但是如果使用后没有及时将赋值的外部变量为null
减少引用计数,也将导致内存泄漏
3 事件监听未移除
function addEvent (){
const node = document.getElementById('warp');
node.addEventListener('touchmove',()=>{
console.log('In Move');
})
}
const onTouchEnd = (){
const node = document.getElementById('warp');
node.
}
useEffect(()=>()=>{
const node = document.getElementById('warp');
node.removeEventListener('touchmove');
}) // 类似react 生命周期函数: componentWillUnmount
render(<div id='warp' onTouchEnd={onTouchEnd}>
// code...
</div>')
4 计时器相关
定时器setInterval
或者setTimeout
在不需要使用的时候,没有被clear,导致定时器的回调函数及其内部依赖的变量都不能被回收,这就会造成内存泄漏。
解决方式:当不需要interval或者timeout的时候,调用clearInterval
或者clearTimeout
5 DOM元素可能出现的引用问题
let a = document.getElementById('a');
document.body.removeChild(a); // dom删除了
console.log(a, 'a'); // 但是还存在引用能console出整个div 没有被回收
a = null;// 解除引用
dom
虽然移除了,但是依然保持引用关系,注意将变量的引用计数减少
var a = {};
document.getElementById('id').diyProp = a;
在window.unonload
事件中加上 document.getElementById('id').diyProp = null
减少引用计数
三 内存泄漏识别方法
怎样可以观察到内存泄漏呢?
经验法则是,如果连续五次垃圾回收之后,内存占用一次比一次大,就有内存泄漏。这就要求实时查看内存占用。
1 浏览器
Chrome 浏览器查看内存占用,按照以下步骤操作。
- 打开开发者工具,选择 Timeline(现在已更名为Performance ) 面板
- 在顶部的
Capture
字段里面勾选 Memory- 点击左上角的录制按钮。
- 在页面上进行各种操作,模拟用户的使用情况。
- 一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况。
如果内存占用基本平稳,接近水平,就说明不存在内存泄漏。
反之,就是内存泄漏了。
2 命令行
命令行可以使用 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
字段为准。
参考文档: