开发者需要依靠弱引用解决垃圾回收相关的内存管理问题。
- 强引用:默认的引用类型,被强引用的对象不会被垃圾回收。
- 弱引用:允许对象在没有其他强引用时被垃圾回收,不会阻止垃圾回收器回收该对象。
场景一:使用弱引用打破循环引用,确保对象能够被垃圾回收。
循环引用
只要这个循环引用存在,即使外部没有这两个对象的强引用,它们也不会被垃圾回收。
class Student {
teacher: Teacher | null = null;
}
class Teacher {
student: Student | null = null;
}
const sTom = new Student();
const sMichael = new Teacher();
sTom.teacher = sMichael;
sMichael.student = sTom;
使用弱引用打破循环引用
由于使用了WeakRef弱引用,只要没有其他强引用指向这个两个对象,它们就可以被垃圾回收,只要有一方使用弱引用,即可解除两者的循环引用。
class Parent {
child: WeakRef<Child> | null = null;
}
class Child {
parent: WeakRef<Parent> | null = null;
}
const pMissWang = new Parent();
const cMaria = new Child();
// 使用 WeakRef 包装对象引用
pMissWang.child = new WeakRef(cMaria);
cMaria.parent = new WeakRef(pMissWang);
场景二:WeakMap和WeakSet仅持有对对象的弱引用,和WeakRef一样它们也不会阻止垃圾回收。
WeakSet和WeakMap都是用于存储弱引用的集合类型,WeakSet存储对象,WeakMap存储键值对。
与WeakRef的区别:WeakSet和WeakMap可用来保存引用对象,但不支持取出对象WeakRef支持存取对象。
WeakSet
不能从 WeakSet 中取出或获取某个对象,只能检查是否存在于集合中使用 WeakSet 存储对象并检查是否存在,可以依据这一特性判断对象是否被垃圾回收。
interface Student {
name: string;
}
const sAlice: Student = { name: 'Alice' };
const sBob: Student = { name: 'Bob' };
const studentWeakSet = new WeakSet<Student>();
// 将学生对象添加到 WeakSet 中
studentWeakSet.add(sAlice);
studentWeakSet.add(sBob);
// 检查学生对象是否存在于 WeakSet 中
console.log('sAlice exists:', studentWeakSet.has(sAlice)); // 输出: true
console.log('sBob exists:', studentWeakSet.has(sBob)); // 输出: true
// 创建一个新的学生对象(不在 WeakSet 中)
const sEve: Student = { name: 'Eve' };
// 检查新学生对象是否存在于 WeakSet 中
console.log('sEve exists:', studentWeakSet.has(sEve)); // 输出: false
// 删除一个学生对象
studentWeakSet.delete(sAlice);
// 再次检查删除后的学生对象是否存在于 WeakSet 中
console.log('sAlice exists:', studentWeakSet.has(sAlice)); // 输出: false
WeakMap
不能列出WeakMap中的所有键或值,只能通过已知的键来访问对应的值。使用WeakMap存储键值对并通过已知的键访问值。
interface Teacher {
name: string;
}
const tSmith: Teacher = { name: 'Mr. Smith' };
const tJohnson: Teacher = { name: 'Ms. Johnson' };
const teacherCourseMap = new WeakMap<Teacher, string>();
// 将教师对象和对应的课程信息添加到 WeakMap 中
teacherCourseMap.set(tSmith, 'Math');
teacherCourseMap.set(tJohnson, 'History');
// 通过已知的教师对象获取对应的课程信息
console.log('tSmith teaches:', teacherCourseMap.get(tSmith)); // 输出: Math
console.log('tJohnson teaches:', teacherCourseMap.get(tJohnson)); // 输出: History
// 创建一个新的教师对象作为键(不在 WeakMap 中)
const tBrown: Teacher = { name: 'Mr. Brown' };
// 尝试通过新的教师对象获取课程信息
console.log('tBrown teaches:', teacherCourseMap.get(tBrown)); // 输出: undefined
WeakRef
用WeakRef创建对象的弱引用,支持在需要时获取原始对象,必须在对象仍然存在时才能获取,如果被回收需要重新创建,一般采用WeakRef的deref()方法获取原始对象。
class FamilyMember {
}
// 存储家庭成员的弱引用
const parentWeakRef = new WeakRef<FamilyMember>(new FamilyMember());
// 获取原始家庭成员对象
const parent = parentWeakRef.deref();
// 检查是否存在目标家庭成员对象,如果目标对象已经被回收,deref() 将返回 undefined。
if (parent) {
console.log('Family member exists');
} else {
console.log('Family member does not exist');
}
场景三:弱引用的垃圾回收时机
WeakRef 主要用于允许对象被垃圾回收,但不保证对象何时会被回收,其垃圾回收时机是不确定的。
由于WeakRef不能检测对象何时被垃圾回收,没有提供与垃圾回收事件关联的回调机制,所以只能通过主动轮询的方式定期检查对象是否已经被回收。
interface User {
username: string
}
const user: User = { username: "john_doe" }
const userWeakRef = new WeakRef(user)
//用定时器轮训检测对象是否被回收
const thetime = setInterval(() => {
if (userWeakRef.deref() === undefined) {
console.log('Object has been disappear');
clearInterval(thetime);//结束轮询
}
}, 1000);
HarmonyOS可用FinalizationRegistry实现类似监控对象生命周期的能力。
使用FinalizationRegistry注册对象后,当对象被垃圾回收时,会调用提供的回调函数。
// 定义回调函数,当目标对象被垃圾回收时会调用该函数
function cleanupCallback(heldValue: number) {
console.log(`Callback received: ${heldValue}`);
}
export function demoFinalizationRegistry() {
// 创建一个 FinalizationRegistry 实例,传入回调函数
const finalizationRegistry = new FinalizationRegistry<number>(cleanupCallback);
// 定义用于注册和取消注册的类
class TargetClass {
}
class UnregisterTokenClass {
}
// 创建对象实例
let targetObject: TargetClass = new TargetClass();
let unregisterToken: UnregisterTokenClass = new UnregisterTokenClass();
// 设置传给回调函数的具体数据
let dataToCleanup = 1;
// 注册对象到 FinalizationRegistry
finalizationRegistry.register(targetObject, dataToCleanup, unregisterToken);
// 取消注册
finalizationRegistry.unregister(unregisterToken);
}