You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现WeakTupleSet?使元组随元素外部引用回收自动移除

实现WeakTupleSet:自动回收包含可被垃圾回收元素的元组

我们需要实现一个WeakTupleSet,功能类似现有的TupleSet(存储顺序无关的唯一元组),但核心区别是:当元组中任意一个元素在集合外部不再被引用时,该元组会自动从集合中移除,对应的元素也能被垃圾回收机制处理。

实现核心思路

  • 用WeakRef包装元组中的每个元素,避免强引用导致元素无法被回收
  • 借助FinalizationRegistry监听元素的垃圾回收事件,一旦元素被回收,立即清理集合中所有包含该元素的元组
  • 存储结构记录元组长度、元素的WeakRef集合,同时保留元组唯一性判断逻辑(长度相同且元素完全匹配,顺序无关)
  • 每次执行操作前,先过滤掉已存在回收元素的无效元组

代码实现

export class WeakTupleSet {
    // 存储元组的弱引用结构:长度、元素WeakRef数组、清理令牌
    private tupleEntries = new Set<{
        length: number;
        weakRefs: WeakRef<unknown>[];
        cleanupToken: symbol;
    }>();

    // 元素回收监听器,触发时清理关联元组
    private finalizationRegistry = new FinalizationRegistry((cleanupToken: symbol) => {
        this.tupleEntries.forEach(entry => {
            if (entry.cleanupToken === cleanupToken) {
                this.tupleEntries.delete(entry);
            }
        });
    });

    // 检查目标元组与存储条目是否匹配
    #isMatchingTuple(targetTuple: unknown[], entry: typeof this.tupleEntries extends Set<infer T> ? T : never): boolean {
        if (targetTuple.length !== entry.length) return false;
        
        // 过滤出仍存活的元素
        const aliveElements = entry.weakRefs.map(ref => ref.deref()).filter(val => val !== undefined);
        // 有元素已回收则直接不匹配
        if (aliveElements.length !== entry.length) return false;

        // 验证目标元组所有元素都在存活列表中
        for (const val of targetTuple) {
            if (!aliveElements.includes(val)) return false;
        }
        return true;
    }

    add(tuple: unknown[]): boolean {
        this.#cleanupInvalidEntries();

        // 检查是否已有匹配元组
        for (const entry of this.tupleEntries) {
            if (this.#isMatchingTuple(tuple, entry)) {
                return false;
            }
        }

        // 创建弱引用条目并注册回收监听
        const weakRefs = tuple.map(item => new WeakRef(item));
        const cleanupToken = Symbol();
        
        tuple.forEach(item => {
            this.finalizationRegistry.register(item, cleanupToken);
        });

        this.tupleEntries.add({ length: tuple.length, weakRefs, cleanupToken });
        return true;
    }

    delete(tuple: unknown[]): boolean {
        this.#cleanupInvalidEntries();

        for (const entry of this.tupleEntries) {
            if (this.#isMatchingTuple(tuple, entry)) {
                this.tupleEntries.delete(entry);
                // 取消该元组元素的回收监听(性能优化)
                entry.weakRefs.forEach(ref => {
                    const val = ref.deref();
                    if (val) this.finalizationRegistry.unregister(entry.cleanupToken);
                });
                return true;
            }
        }
        return false;
    }

    has(tuple: unknown[]): boolean {
        this.#cleanupInvalidEntries();

        for (const entry of this.tupleEntries) {
            if (this.#isMatchingTuple(tuple, entry)) {
                return true;
            }
        }
        return false;
    }

    // 清理所有包含已回收元素的无效元组
    #cleanupInvalidEntries(): void {
        this.tupleEntries.forEach(entry => {
            const hasCollected = entry.weakRefs.some(ref => ref.deref() === undefined);
            if (hasCollected) {
                this.tupleEntries.delete(entry);
            }
        });
    }
}

使用示例

const set = new WeakTupleSet();

globalThis.obj1 = {};
globalThis.obj2 = {};
globalThis.obj3 = {};

const tuple = [globalThis.obj1, globalThis.obj2, globalThis.obj3];

set.add(tuple);
console.log(set.has(tuple)); // 输出: true

// 取消对obj2的外部引用
tuple[1] = undefined;
delete globalThis.obj2;

// 手动触发垃圾回收(仅Node.js加--expose-gc参数或浏览器开发者工具有效)
if (globalThis.gc) globalThis.gc();

// 等待GC自动执行后,元组会被移除
setTimeout(() => {
    console.log(set.has(tuple)); // 输出: false
}, 1000);

注意事项

  • FinalizationRegistry的回调执行时机由垃圾回收机制决定,无法精确控制
  • 元组元素必须是可被垃圾回收的对象类型,原始值(如字符串、数字)无法被WeakRef包装,不适用此实现
  • 生产环境无需手动调用gc(),依赖浏览器/Node.js的自动垃圾回收逻辑

内容的提问来源于stack exchange,提问作者trusktr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 11:22:03