开发者需要依靠弱引用解决垃圾回收相关的内存管理问题。

  • 强引用:默认的引用类型,被强引用的对象不会被垃圾回收。
  • 弱引用:允许对象在没有其他强引用时被垃圾回收,不会阻止垃圾回收器回收该对象。

场景一:使用弱引用打破循环引用,确保对象能够被垃圾回收

循环引用

只要这个循环引用存在,即使外部没有这两个对象的强引用,它们也不会被垃圾回收。

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);
}

HarmonyOS码上奇行
7k 声望2.8k 粉丝