文章目录
- 垃圾回收
- 垃圾回收原理
- 常见的垃圾回收方法
- 1. 标记清除算法(Mark and Sweep)
- 2. 标记整理算法(Mark and Compact)
- 3. 复制算法(Copying)
- 4. 分代回收算法(Generational Garbage Collection)
- 手动触发垃圾回收
- js中对象怎么做到弱引用
- 1. `WeakMap`
- 基本语法
- 示例代码
- 使用场景
- 2. `WeakSet`
- 基本语法
- 示例代码
- 使用场景
- 垃圾回收与弱引用之间的关系
- 弱引用与垃圾回收的关系
- 强引用与垃圾回收
- 弱引用与垃圾回收(WeakMap)
- 应用场景
- 1. 缓存数据
- 2. 实现私有属性和方法
- 3. 跟踪对象状态
- 弱引用与垃圾回收(WeakSet )
- 1. `WeakSet` 的基本概念
- 2. `WeakSet` 与垃圾回收的关系
- 强引用和弱引用对垃圾回收的影响
- 垃圾回收时机
- 3. `WeakSet` 的特性与垃圾回收的关联
- 4. 示例代码
- 5. 应用场景
垃圾回收
在 JavaScript 中,垃圾回收(Garbage Collection,简称 GC)是自动管理内存的机制,它负责回收不再被程序使用的内存空间,以避免内存泄漏,提高内存使用效率。下面详细介绍 JavaScript 垃圾回收的原理和常见方法。
垃圾回收原理
JavaScript 是一种使用自动内存管理的语言,其垃圾回收的核心原理基于可达性分析。即判断一个对象是否可以被程序访问到,如果一个对象无法通过任何可达的引用路径被访问,那么这个对象就被认为是“垃圾”,可以被回收。
常见的垃圾回收方法
1. 标记清除算法(Mark and Sweep)
这是最基础也是最常见的垃圾回收算法,现代浏览器大多采用这种算法的改进版本。其工作过程分为两个阶段:
- 标记阶段:垃圾回收器从根对象(如全局对象
window
)开始,遍历所有可达对象,并为它们标记。根对象是指那些在程序中可以直接访问的对象,例如全局变量、函数调用栈中的变量等。 - 清除阶段:垃圾回收器遍历整个内存空间,将未被标记的对象视为垃圾,并释放它们所占用的内存空间。
示例解释:
// 创建一个对象
let obj1 = { name: 'object1' };
let obj2 = { name: 'object2' };// obj1 引用 obj2
obj1.ref = obj2;// 移除对 obj2 的直接引用
obj2 = null;// 此时,obj2 仍然可以通过 obj1.ref 访问到,所以不会被标记为垃圾// 移除对 obj1 的引用
obj1 = null;// 现在 obj1 和 obj2 都无法从根对象访问到,在下一次垃圾回收时,它们将被标记并清除
2. 标记整理算法(Mark and Compact)
标记整理算法是标记清除算法的改进版本,它解决了标记清除算法可能导致的内存碎片化问题。其工作过程分为三个阶段:
- 标记阶段:与标记清除算法相同,从根对象开始遍历,标记所有可达对象。
- 整理阶段:将所有存活的对象向内存的一端移动,使它们连续存储,从而消除内存碎片。
- 清除阶段:释放所有未被标记的对象所占用的内存空间。
3. 复制算法(Copying)
复制算法将内存空间分为两个相等的区域:一个是使用区(From Space),另一个是空闲区(To Space)。其工作过程如下:
- 当程序运行时,所有对象都分配在使用区。
- 当使用区内存不足时,垃圾回收器开始工作。它从根对象开始遍历,将所有存活的对象复制到空闲区。
- 复制完成后,交换使用区和空闲区的角色,原来的使用区变为空闲区,原来的空闲区变为使用区。
优点:复制算法的效率较高,因为它只需要移动存活的对象,并且不会产生内存碎片。
缺点:需要额外的内存空间,因为要将内存分为两个区域,实际可用的内存只有一半。
4. 分代回收算法(Generational Garbage Collection)
分代回收算法基于一个经验观察:大多数对象的生命周期都很短,而少数对象的生命周期很长。因此,该算法将对象分为不同的代(Generation),不同代采用不同的垃圾回收策略。
- 新生代(Young Generation):新创建的对象通常被分配到新生代。新生代使用复制算法进行垃圾回收,因为新生代中的对象大多很快就会死亡,复制算法可以高效地处理这些对象。
- 老生代(Old Generation):经过多次垃圾回收仍然存活的对象会被晋升到老生代。老生代使用标记清除或标记整理算法进行垃圾回收,因为老生代中的对象生命周期较长,复制算法的效率较低。
手动触发垃圾回收
在 JavaScript 中,通常不建议手动触发垃圾回收,因为现代浏览器和 JavaScript 引擎会自动管理垃圾回收的时机,以优化性能。不过,在某些环境中(如 Node.js),可以使用以下方法手动触发垃圾回收:
if (global.gc) {global.gc();
}
需要注意的是,手动触发垃圾回收可能会影响程序的性能,应该谨慎使用。
js中对象怎么做到弱引用
在 JavaScript 里,ES6 引入了 WeakMap
和 WeakSet
这两种数据结构来实现对象的弱引用。下面分别介绍它们的使用方式和特点。
1. WeakMap
WeakMap
是一种键值对集合,它的键必须是对象,并且这些对象是弱引用的。当对象的其他强引用都被移除后,WeakMap
中的键对象会被垃圾回收,对应的键值对也会自动从 WeakMap
中移除。
基本语法
const weakMap = new WeakMap();
示例代码
// 创建一个对象
const obj1 = {};
const obj2 = {};// 创建一个 WeakMap
const weakMap = new WeakMap();// 向 WeakMap 中添加键值对
weakMap.set(obj1, 'value for obj1');
weakMap.set(obj2, 'value for obj2');// 获取值
console.log(weakMap.get(obj1)); // 输出: 'value for obj1'// 检查某个键是否存在
console.log(weakMap.has(obj2)); // 输出: true// 删除键值对
weakMap.delete(obj1);
console.log(weakMap.has(obj1)); // 输出: false// 当对象的其他强引用被移除后,WeakMap 中的键值对会自动被清理
let key = { name: 'temp' };
weakMap.set(key, 'temporary value');
key = null; // 移除对对象的强引用
// 一段时间后(垃圾回收执行时),该键值对会从 WeakMap 中消失
使用场景
- 缓存数据:当你需要为对象关联一些额外的数据,并且不希望这些数据阻止对象被垃圾回收时,可以使用
WeakMap
。 - 私有数据存储:可以使用
WeakMap
来模拟对象的私有属性。
2. WeakSet
WeakSet
是一种只存储对象的集合,这些对象也是弱引用的。与 WeakMap
类似,当对象的其他强引用被移除后,对象会从 WeakSet
中自动移除。
基本语法
const weakSet = new WeakSet();
示例代码
// 创建对象
const objA = {};
const objB = {};// 创建一个 WeakSet
const weakSet = new WeakSet();// 向 WeakSet 中添加对象
weakSet.add(objA);
weakSet.add(objB);// 检查对象是否存在于 WeakSet 中
console.log(weakSet.has(objA)); // 输出: true// 删除对象
weakSet.delete(objB);
console.log(weakSet.has(objB)); // 输出: false// 当对象的其他强引用被移除后,WeakSet 中的对象会自动被清理
let item = { id: 1 };
weakSet.add(item);
item = null; // 移除对对象的强引用
// 一段时间后(垃圾回收执行时),该对象会从 WeakSet 中消失
使用场景
- 跟踪对象状态:可以使用
WeakSet
来跟踪某些对象是否处于某个特定状态,而不会影响对象的生命周期。
需要注意的是,WeakMap
和 WeakSet
都没有迭代器,不能使用 for...of
循环,也没有 size
属性,因为它们的主要目的是提供弱引用功能,避免影响对象的垃圾回收。
垃圾回收与弱引用之间的关系
弱引用与垃圾回收的关系
在 JavaScript 中,垃圾回收(Garbage Collection,简称 GC)是自动管理内存的机制,其核心原则是回收不再被程序使用的内存空间,以避免内存泄漏。而弱引用是一种特殊的引用方式,它不会阻止对象被垃圾回收,二者存在紧密的联系:
强引用与垃圾回收
在正常情况下,当一个对象被其他变量或数据结构引用时,这种引用是强引用。只要存在强引用指向一个对象,该对象就不会被垃圾回收机制回收。例如:
const obj = { name: 'example' };
// 这里 obj 对对象 { name: 'example' } 是强引用
// 只要 obj 存在,这个对象就不会被垃圾回收
弱引用与垃圾回收(WeakMap)
JavaScript 中的 WeakMap
和 WeakSet
实现了弱引用。当对象仅被 WeakMap
或 WeakSet
引用时,这些引用不会阻止对象被垃圾回收。一旦对象的所有强引用都被移除,垃圾回收机制会在合适的时机回收该对象,同时 WeakMap
或 WeakSet
中对应的引用也会自动消失。例如:
const weakMap = new WeakMap();
let key = { id: 1 };
weakMap.set(key, 'value');
// 此时 key 对对象 { id: 1 } 是强引用,weakMap 对它是弱引用key = null;
// 移除了对对象 { id: 1 } 的强引用
// 当垃圾回收执行时,对象 { id: 1 } 会被回收,weakMap 中对应的键值对也会自动移除
应用场景
1. 缓存数据
在开发中,有时需要为某些对象缓存一些额外的数据,但又不希望缓存的数据阻止对象被垃圾回收。使用 WeakMap
可以很好地实现这一需求。
// 假设我们有一个函数需要为对象缓存一些计算结果
const cache = new WeakMap();function calculateAndCache(obj) {if (cache.has(obj)) {return cache.get(obj);}// 进行一些复杂的计算const result = /* 复杂计算逻辑 */ obj.id * 2;cache.set(obj, result);return result;
}let myObject = { id: 5 };
const result = calculateAndCache(myObject);
console.log(result);myObject = null;
// 当 myObject 的强引用被移除后,它和对应的缓存数据都会被垃圾回收
2. 实现私有属性和方法
WeakMap
可以用来模拟对象的私有属性和方法,因为外部无法直接访问 WeakMap
中的数据,并且不会影响对象的生命周期。
const privateData = new WeakMap();class MyClass {constructor() {privateData.set(this, {secret: 'This is a private value'});}getSecret() {return privateData.get(this).secret;}
}const instance = new MyClass();
console.log(instance.getSecret()); instance = null;
// 当 instance 的强引用被移除后,对象和对应的私有数据都会被垃圾回收
3. 跟踪对象状态
WeakSet
可以用于跟踪某些对象是否处于某个特定状态,而不会影响对象的生命周期。例如,在一个游戏中,跟踪哪些角色已经被标记为死亡。
const deadCharacters = new WeakSet();class Character {constructor(name) {this.name = name;}die() {deadCharacters.add(this);}isDead() {return deadCharacters.has(this);}
}const character1 = new Character('Hero');
character1.die();
console.log(character1.isDead()); character1 = null;
// 当 character1 的强引用被移除后,它会从 deadCharacters 中自动移除
通过使用弱引用,可以更灵活地管理内存,避免因不必要的引用导致的内存泄漏问题。
弱引用与垃圾回收(WeakSet )
1. WeakSet
的基本概念
WeakSet
是 JavaScript 中的一种数据结构,它是一个集合,只能存储对象,并且这些对象是弱引用的。这意味着 WeakSet
中的对象不会阻止垃圾回收机制对它们的回收。
2. WeakSet
与垃圾回收的关系
强引用和弱引用对垃圾回收的影响
- 强引用:当一个对象被其他变量或数据结构以常规方式引用时,这种引用是强引用。只要存在强引用指向一个对象,该对象就不会被垃圾回收。例如:
const obj = { key: 'value' };
// 这里 obj 对 { key: 'value' } 是强引用,只要 obj 存在,这个对象就不会被回收
- 弱引用:
WeakSet
中的对象是弱引用。如果一个对象仅被WeakSet
引用,而没有其他强引用指向它,那么该对象就可以被垃圾回收。当对象被垃圾回收后,它会自动从WeakSet
中移除。例如:
const weakSet = new WeakSet();
let obj = { key: 'value' };
weakSet.add(obj);
// 此时 obj 对 { key: 'value' } 是强引用,weakSet 对它是弱引用obj = null;
// 移除了对对象 { key: 'value' } 的强引用
// 当垃圾回收执行时,对象 { key: 'value' } 会被回收,并且会自动从 weakSet 中移除
垃圾回收时机
JavaScript 的垃圾回收机制是自动且不可预测的,它会在内存达到一定阈值或者空闲时触发。因此,我们无法精确知道对象何时会从 WeakSet
中被移除,但可以确定的是,一旦对象的所有强引用被移除,它最终会被回收,并且会从 WeakSet
中消失。
3. WeakSet
的特性与垃圾回收的关联
- 不能遍历:
WeakSet
没有forEach
方法,也不能使用for...of
循环,因为其成员随时可能被垃圾回收,遍历可能会导致结果不一致。 - 没有
size
属性:由于WeakSet
中的对象可能随时被回收,size
属性的值会不断变化,因此WeakSet
没有size
属性。
4. 示例代码
// 创建一个 WeakSet
const weakSet = new WeakSet();// 创建对象
const obj1 = { id: 1 };
const obj2 = { id: 2 };// 将对象添加到 WeakSet 中
weakSet.add(obj1);
weakSet.add(obj2);// 检查对象是否存在于 WeakSet 中
console.log(weakSet.has(obj1)); // 输出: true// 移除对 obj1 的强引用
obj1 = null;// 此时垃圾回收机制可能还未执行,obj1 可能还在 WeakSet 中
// 当垃圾回收执行后,obj1 会被回收,并且会自动从 WeakSet 中移除// 手动触发垃圾回收(在某些环境中可能有效)
if (global.gc) {global.gc();
}// 再次检查 obj1 是否存在于 WeakSet 中
console.log(weakSet.has(obj1)); // 输出: false(假设垃圾回收已执行)
5. 应用场景
- 跟踪对象状态:可以使用
WeakSet
来跟踪某些对象是否处于某个特定状态,而不会影响对象的生命周期。例如,在一个图形库中,跟踪哪些图形元素已经被标记为隐藏。
const hiddenElements = new WeakSet();class GraphicElement {constructor(id) {this.id = id;}hide() {hiddenElements.add(this);}isHidden() {return hiddenElements.has(this);}
}const element = new GraphicElement(1);
element.hide();
console.log(element.isHidden()); element = null;
// 当 element 的强引用被移除后,它会从 hiddenElements 中自动移除
通过使用 WeakSet
,可以避免因不必要的引用导致的内存泄漏问题,让对象在不再被使用时能够及时被垃圾回收。