虽然前端有垃圾回收机制,但当某块无用的内存,却无法被垃圾回收机制认为是垃圾时,也就发生内存泄漏了
而垃圾回收机制通常是使用标志清除策略,简单说,也就是引用从根节点开始是否可达来判定是否是垃圾。
一、浏览器的垃圾回收机制
1.垃圾回收算法
引用计数法
- 原理:机制就是跟踪一个值的引用次数,当声明一个变量并将一个引用类型赋值给该变量时该值引用次数加1,当这个变量指向其他一个时该值的引用次数便减一。当该值引用次数为0时就会被回收
- 问题:该方式会引起内存泄漏的原因是它不能解决循环引用的问题,一般现代浏览器都不使用改种方式(IE6)
var a={};var b={};a.prop = b;b.prop = a;
标记清除法(Mark-and-sweep)
- 大部分浏览器以此方式进行垃圾回收,当变量进入执行环境(函数中声明变量)的时候,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”,在离开环境之后还有的变量则是需要被删除的变量。

2.GC根:
一般指全局且不会被垃圾回收的对象,比如:window、document或者是页面上存在的dom元素。JavaScript的垃圾回收算法会判断某块对象内存是否是GC根可达(存在一条由GC根对象到该对象的引用),任何无法从根到达的对象都会被 GC 回收。

3. 内存支配

- 节点 1 支配节点 2
- 节点 2 支配节点 3 、4 和 6
- 节点 3 支配节点 5
- 节点 5 支配节点 8
- 节点 6 支配节点 7
二、内存泄漏的常见场景
1. 意外的全局变量
function foo(arg) {bar = "this is a hidden global variable with a large of data";}// 等价于function foo(arg) {window.bar = "this is an explicit global variable with a large of data";}function foo() {this.variable = "potential accidental global";}foo();//等价于function foo(arg) {window.bar = "this is a explicit global variable with a large of data";}// 之前哲文代码review的时候探讨问过这样的写法(function(w,$){// doSomething})(window, jQuery)//1.显示注明了依赖,可维护性大大提高//2.避免了闭包直接引用全局外部变量
函数里引用全局变量的问题,需要尽量避免,一般我们都可以使用严格模式来写代码,还有eslint来保证,所以现在知道了eslint的重要性。
2.遗忘的定时器
setTimeout 和 setInterval 是由浏览器专门线程来维护它的生命周期,所以当在某个页面使用了定时器,当该页面销毁时,没有手动去释放清理这些定时器的话,那么这些定时器还是存活着的。也就是说,定时器的生命周期并不挂靠在页面上,所以当在当前页面的 js 里通过定时器注册了某个回调函数,而该回调函数内又持有当前页面某个变量或某些 DOM 元素时,就会导致即使页面销毁了,由于定时器持有该页面部分引用而造成页面无法正常被回收,从而导致内存泄漏了。
// 5秒拉一次数据var serverData = new Array(1000000).join('*')setInterval(function() {var renderer = document.getElementById('renderer');if(renderer) {renderer.innerHTML = JSON.stringify(serverData);}}, 5000);
3.使用不当的闭包
函数本身会持有它定义时所在的词法环境的引用,但通常情况下,使用完函数后,该函数所申请的内存都会被回收了
但当函数内再返回一个函数时,由于返回的函数持有外部函数的词法环境,而返回的函数又被其他生命周期东西所持有,导致外部函数虽然执行完了,但内存却无法被回收
建议:在传递给console.log的对象是不能被垃圾回收 ♻️,因为在代码运行之后需要在开发工具能查看对象信息。所以最好不要在生产环境中console.log任何对象。
4.遗漏的 DOM 元素
DOM 元素的生命周期正常是取决于是否挂载在 DOM 树上,当从 DOM 树上移除时,也就可以被销毁回收了
但如果某个 DOM 元素,在 js 中也持有它的引用时,那么它的生命周期就由 js 和是否在 DOM 树上两者决定了,记得移除时,两个地方都需要去清理才能正常回收它
2.内存泄漏的判断方法
- 通过性能监控工具排查(判断有无)
- performace monitor
- performance memory视图
- 在
Performance面板记录性能时,勾选memory即可在分析结果中看到 memory 占用情况



