文章目录
js内存泄露如何检测?场景有哪些?如何定位到具体代码?
定义
程序中已动态分配的堆内存由于某种原因程序未释放或无法释放引发的各种问题,如浏览器变慢,奔溃,延迟大
引起内存泄漏的操作
- 闭包函数使用不当
- 未使用的 var 声明的全局变量
- 分离的DOM节点
- 控制台日志(
console.log
)- 遗忘定时器
Chrome 控制台查看内存情况
无痕模式
打开Chrome的无痕模式,这样做的目的是为了屏蔽掉Chrome插件对我们之后测试内存占用情况的影响
Performance
在页面从零到加载完成这个过程中
JS Heap
(js堆内存)、
documents
(文档)、
Nodes
(DOM节点)、
Listeners
(监听器)、
GPU
memory(GPU内存)的最低值、最高值以及随时间的走势曲线
Memotry
作用:用于记录页面堆内存的具体情况以及js堆内存随加载时间线动态的分配情况
1. 先用 Allocation instrumentation on timeline 确认问题
在开始记录后,我们可以看到图中右上角有起伏的蓝色与灰色的柱形图,其中
蓝色
表示当前时间线下占用着的内存;
灰色
表示之前占用的内存空间已被清除释放
2. 再用 Head snapshot 定位代码
闭包函数使用不当
// 其中,arr被赋值给 res,arr 被标记为活动变量并一直占用着相应的内存,假如 res 后续用不到,这内存就一直占用
<button onclick="btnClick()">点击</button>
<script>
function test() {
let arr = new Array(1000000);// 一个很大的数组对象
function fn() {
let c = [1, 2, 3]
}
fn()
return arr
}
let res = []
function btnClick() {
res.push(test())
}
</script>
-
先使用 performacne 的曲线图
在每次录制开始时手动触发一次
垃圾回收机制
,这是为了确认一个初始的
堆内存基准线
,便于后面的对比,然后我们点击了几次按钮,即往全局数组变量res中添加了几个比较大的数组对象,最后
再触发一次垃圾回收
,发现录制结果的
JS Heap
曲线刚开始成阶梯式上升的,最后的
曲线的高度比基准线要高
,说明可能是存在内存泄漏的问题
-
用 Allocation instrumentation on timeline 手机动态内存分配情况
动态内存分配情况图上都会出现一个蓝色的柱形,并且在我们触发垃圾回收后,蓝色柱形都
没变成灰色柱形
,即之前分配的内存并未被清除,确认内存泄漏的问题是存在的
-
用 Head snapshot 定位
所以选择Object allocated between Snapshot1 and Snapshot2即展示第一条快照和第二条快照存在
差异的内存对象分配
情况,Array的
百分比
很高,初步可以判断是该变量存在问题
全局变量
全局变量的内存空间一直不会被释放
分离的DOM节点
<div id="root">
<div class="child">我是子元素</div>
<button>移除</button>
</div>
<script>
let btn = document.querySelector('button')
let child = document.querySelector('.child')
let root = document.querySelector('#root')
btn.addEventListener('click', function () {
root.removeChild(child)
})
</script>
点击按钮后移除.child的节点,虽然点击后,该节点确实从dom被移除了,但全局变量child仍对该节点
有引用
,所以导致该节点的内存一直无法被释放
控制台的打印
<button>按钮</button>
<script>
document.querySelector('button').addEventListener('click', function () {
let obj = new Array(1000000)
console.log(obj);
})
</script>
先触发一次垃圾回收清除初始的内存,然后点击三次按钮,即执行了三次点击事件,最后再触发一次垃圾回收。查看录制结果发现JS Heap曲线成阶梯上升,并且最终保持的高度比初始基准线高很多,这说明每次执行点击事件创建的很大的数组对象obj都因为console.log被浏览器保存了下来并且
无法被回收
避免策略
- 减少不必要的全局变量
- 避免”死循环”
- 避免创建过多的对象
- 减少层级过多的引用
- 避免定时器遗忘
参考