目录
一、垃圾回收机制的基本原理
(1)基本原理理解
(2)回收
二、垃圾回收的算法
1.标记清除算法
2.引用计数算法
三、减少垃圾回收
(1)减少对象创建
(2)优化数据结构及内存分配优化
(3)避免内存泄漏
四、避免内存泄漏的建议
(1)导致内存泄漏
(2)避免内存泄漏
前提:前端的垃圾回收机制主要指的是浏览器中的JavaScript引擎所实现的内存管理机制。这种机制自动回收不再使用的内存空间,以避免内存泄漏和内存溢出等问题。以下是JavaScript垃圾回收机制的详细介绍。
一、垃圾回收机制的基本原理
(1)基本原理理解
浏览器的JavaScript引擎具有自动垃圾回收机制,该机制会定期找出那些不再使用的变量和对象,并释放它们所占用的内存。垃圾回收的主要目标是识别和回收那些已经不可达的对象,即无法从根对象出发直接或间接访问到。
以下是一个简单的JavaScript代码示例,用于说明垃圾回收机制的工作原理:
let dog = new Object();
dog.a = new Array(1);// 这时dog和dog.a都是可达对象,不会被回收。dog.a = new Object();// 之前创建的数组对象成为了不可达对象,将被垃圾回收机制回收。
在这个例子中,当dog.a
被重新赋值为一个新的对象时,之前与dog.a
关联的数组对象就变成了不可达对象。在垃圾回收过程中,这个数组对象将被标记为垃圾并被回收。
(2)回收
- 定期会处理不在使用的变量、对象,释放占用的内存。
- 两种变量:全局变量和局部变量。全局变量会持续到页面卸载;局部变量在声明函数中,从函数执行开始,直到函数执行结束,就会被释放。
- 特殊情况:当局部变量被外部函数使用时,例如闭包,在函数执行结束后,函数外部的变量仍然指向函数内部的局部变量,此时局部变量仍被使用,所以不会回收。
二、垃圾回收的算法
浏览器中的JavaScript引擎通常使用两种垃圾回收算法:标记清除(Mark-and-Sweep)和引用计数(Reference Counting)。现代浏览器主要使用标记清除算法,因为引用计数算法在处理循环引用时会出现问题。
1.标记清除算法
(1)标记阶段:从根对象,如全局对象、活动函数栈等开始,遍历内存中的对象,并给所有可达的对象打上标记。
(2)清除阶段:遍历整个堆内存,将未被标记的对象进行回收,并释放其占用的内存空间。
2.引用计数算法
每个对象都有一个引用计数器,用于记录当前对象被引用的次数。
当对象被引用时,其引用计数器加1;当对象引用被移除或销毁时,计数器减1。
当对象的引用计数器为0时,即没有任何引用指向它时,该对象被判定为不再被使用,可以回收。
注意:引用计数算法无法处理循环引用的问题。
举例:
// 假如对象A和B
let A = {};
let B = {};// A引用B
A.b = B;// B也引用A
B.a = A;A.b = null; // A不再引用B,但B仍然通过B.a引用A
B.a = null; // B不再引用A,但A已经没有机会知道B已经释放了对它的引用(在纯引用计数算法中)
由于它们相互引用,它们的引用计数器都不会变为0。看似已经断开了所有对A
和B
的外部引用,由于它们之间的循环引用,它们的引用计数器仍然不为0,所以垃圾回收器如果仅依赖引用计数算法将不会回收它们,这就导致了内存泄漏。
三、减少垃圾回收
浏览器能进行垃圾自动回收,但是我们应该尽量减少垃圾回收。
(1)减少对象创建
对象尽量复用,不在使用的对象将其设置为null,尽快被回收。
(2)优化数据结构及内存分配优化
选择合适的数据结构来存储数据,以减少内存占用;
避免使用不必要的嵌套数据结构,简化数据结构层次;
在清空数组时,最简单的是给其赋值为[],但是会创建一个新的空对象。所以使用数组时,尽量通过修改数组长度设为0来清空数组。
(3)避免内存泄漏
及时解除不再需要的事件监听器和回调函数;
清理定时器或第三方库创建的对象,避免它们一直占用内存......
四、避免内存泄漏的建议
(1)导致内存泄漏
意外的全局变量
被遗忘的计时器或回调函数
脱离DOM的引用
闭包
(2)避免内存泄漏
减少全局变量的使用:全局变量会一直存在于内存中,直到页面关闭。
避免循环引用:循环引用会导致内存泄漏,因为垃圾回收器无法回收这些对象。
及时清理事件监听器和定时器:未清理的事件监听器和定时器会一直存在于内存中。
注意闭包的使用:闭包会保留对外部函数的引用,可能导致外部函数的变量无法被垃圾回收。
优化DOM操作:避免频繁的DOM操作,减少DOM节点的创建和销毁。
若文章对你有帮助,点赞、收藏加关注吧!