目录
什么是内存泄漏
在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。
通俗点就是:内存泄漏就是一些可以避免的内存花销。
常见的内存泄漏问题
闭包会引起内存泄漏?
不一定,闭包是否产生内存浪费取决于 js 的垃圾回收机制是否将变量占用的内存给及时清除掉,常见的垃圾回收算法有标记清除法,能标记到就不回收,反之则回收。
-
不会造成内存浪费的情况
一般如果闭包函数没有返回值或者没有使用全局变量对闭包函数的返回值进行存储,如下
function fn() {
//fn 函数作用域的局部变量 arr
const arr = new Array(10000000)
function fn1() {
// 在 fn1 函数作用域下访问(依赖)外层 fn 函数作用域的局部变量 arr,此时会产生闭包
console.log(arr[0]);
}
fn1()
}
fn()
打开控制台进行断点查看:
打开控制台进行性能分析:
函数在执行时一开始由于创建了new Array(10000000),所以占据了堆中大概40mb内存,也就是一个Array(10000000)的大小,不过在很快的一段时间内变为了1.5mb,也就是 js 的垃圾回收机制在闭包函数运行后成功后在一段时间后把闭包产生的内存垃圾(new Array(10000000))给清理掉了
-
会造成内存浪费的情况
如果闭包函数存在返回值并且使用全局变量对闭包函数的返回值进行存储,如下
function fn() {
const arr = new Array(10000000)
function fn1() {
console.log(arr[0]);
return new Array(10000000)
}
return fn1
}
// 变量 test 由于保存了 fn1 函数的引用,
// 而 fn1 函数作用域内又依赖于 fn 作用域的Array(10000000),变量 arr 同理
// 所以导致Array(10000000)在堆内存中无法被释放
let test = fn()
let arr = test()
打开控制台进行断点查看:
打开控制台进行性能分析:
80mb大概就是两个 Array(10000000)的内存大小,因为全局变量 test,arr 一直保持对Array(10000000)的引用,所以导致 js 垃圾回收机制无法回收
解决方法:可以使变量赋值为 null
function fn() {
const arr = new Array(10000000)
function fn1() {
console.log(arr[0]);
return new Array(10000000)
}
return fn1
}
// 变量 test 由于保存了 fn1 函数的引用,
// 而 fn1 函数作用域内又依赖于 fn 作用域的Array(10000000),变量 arr 同理
// 所以导致Array(10000000)在堆内存中无法被释放
let test = fn()
let arr = test()
test = null
arr = null
打开控制台进行性能分析:
意外的全局变量
在 JavaScript 中,如果没有使用 var、let 或 const 关键字声明变量,它将成为全局变量,即使它在函数内部声明。如果创建了一个全局变量,但没有及时释放它,那么它将一直存在于内存中,可能导致内存泄漏。
解决方法:使用 var、let 或 const 关键字声明所有的变量,并在不再需要时及时释放它们
function init() {
// create local variable
// myvar = 'hello world' 没有用 var 申明,导致变成全局变量
var myVar = 'hello world';
// add event listener to DOM element
document.getElementById('myButton').addEventListener('click', function() {
// do something with myVar
});
// myVar is no longer needed and will be garbage collected
myVar = null;
}
定时器未清除引起的内存泄露
在使用 JavaScript 定时器时,如果不及时清除定时器,那么它将继续存在并持有内存。这是因为定时器仍然在等待计时器到达指定的时间,这可能会导致内存泄漏。
解决方法:在不再需要定时器时,手动清除它
var timer = setInterval(function() {
// do something
}, 1000);
// clear timer when it's no longer needed
clearInterval(timer);
循环引用引起的内存泄露
当两个或更多对象相互引用时,就会出现循环引用。如果其中一个对象被删除,它仍然被其他对象引用,这可能会导致内存泄漏。
解决方法:在不需要对象时,手动解除相互引用
var obj1 = {};
var obj2 = {};
obj1.ref = obj2;
obj2.ref = obj1;
// remove references to obj1 and obj2 when they're no longer needed
obj1.ref = null;
obj2.ref = null;
obj1 = null;
obj2 = null;
DOM泄露
获取到 DOM 节点之后,将 DOM 节点删除,但是没有手动释放变量,那对应的 DOM 节点在变量中还可以访问到,就会造成泄露。
解决方法:在不需要的时候手动解除对外部变量的引用
function createClosure() {
var element = document.getElementById('myElement');
return function() {
// do something with element
element = null; // remove reference to element
};
}
var closure = createClosure();
// element is no longer referenced and can be garbage collected
前端常见内存泄露检测工具
以下是几个常见的前端内存泄漏检测工具:
- Chrome开发者工具
Chrome浏览器的开发者工具提供了内存分析工具,可以检查应用程序中所有的JavaScript对象,显示它们的引用关系、内存占用情况等信息。在Chrome开发者工具中,可以通过“Memory”选项卡来检测内存泄漏问题。
- Heapdump
Heapdump是一个Node.js模块,用于生成堆快照,并提供了一个用于检查内存泄漏的Web界面。Heapdump可以在运行时捕获应用程序的堆快照,并将其保存到文件中。然后可以使用Chrome开发者工具或其他工具来分析堆快照中的数据。
- LeakChecker
LeakChecker是一款基于Chrome开发者工具的内存泄漏检测工具,可以检测JavaScript应用程序中的内存泄漏问题。LeakChecker跟踪页面中所有的JavaScript对象,并生成报告以帮助用户解决问题。
- Lighthouse
Lighthouse是一款由Google开发的工具,可以用于检查Web应用程序的性能和可访问性。Lighthouse提供了内存分析工具,可以检查JavaScript对象的内存占用情况,并生成报告以帮助用户解决问题。
- DevTools Timeline
DevTools Timeline是Chrome开发者工具中的一个工具,可以用于检查应用程序的性能和内存使用情况。DevTools Timeline跟踪页面中所有的JavaScript对象,并显示它们的内存占用情况。使用DevTools Timeline,开发者可以检查应用程序中的内存泄漏问题,并进行优化。