记忆 GC 详解

wesgine 记忆垃圾回收的淘汰策略、豁免规则与节流调度。


淘汰策略

GC 使用 retentionScore 加权淘汰(INV-MEM-26),不是纯 LRU:

score = timeDecay(accessed_at)
      × recallWeight(recall_count)
      × trustWeight(trust)
      × kindWeight(kind)

分数最低的条目优先被回收。


四个权重因子

timeDecay(时间衰减)

基于 accessed_at(归一后下界为 created_at,INV-MEM-51)。

越久未被召回 → 衰减越大 → 分数越低。

recallWeight(召回频率)

基于 recall_count。

被频繁召回的条目保留分数更高。

trustWeight(信任等级)

Trust权重
TrustSystem最高
TrustExplicit高
TrustMulti中
TrustInferred低
TrustUnknown最低

kindWeight(类型权重)

不同 Kind 有不同的保留倾向。


GC 豁免

以下 Kind 的条目免疫 GC:

Kind原因
KindConstraint用户显式约束,不可自动删除
KindInstruction用户显式指令
KindEnvironment系统环境信息

豁免条目仍然计入配额(Quotas.MemoryMaxEntries)。


TTL 批处理

GC 按 TTL 分批回收:

func gcTTLBatch(scope, namespace string, ttl time.Duration, limit int) {
    // 单次只回收一批
    // 不遍历全量
    // 下次调度继续
}

判据:剩下的会不会被再访问一次。


Digest 触发

GC 同时触发 Digest 压缩:

触发说明
DigestInterval定时触发
GC 运行后GC 完成后检查是否需要 Digest
HTTP 按需POST /memory/digest

Digest 执行:BM25 语义去重、冲突检测、stale 标记、supersession、晋升。


配额联动

QuotaSink 计数规则

操作计数
Save 成功RecordMemoryDelta(+1)
Delete 全部 backend 成功RecordMemoryDelta(-1)
Delete 任一 backend 失败不计数(INV-MEM-33)

配额校正

QuotaGC 每 5 分钟校正:

storeBackoffPersister

写入后重读(幂等 save),防止虚增计数:


"页到空"策略

ClearNamespace / RefreshEnvironment 使用循环至清空:

for {
    batch := list(scope, namespace, limit=500)
    if len(batch) == 0 {
        break
    }
    for _, entry := range batch {
        delete(entry.ID)
    }
}

不用 Limit: 0——读"全部"不会真的只读一次(INV-MEM-34)。


相关不变量


相关文档