Prhub

#25770 [Bug][Mamba] Fix LRU list reference cycle leak in mamba_radix_cache

原始 PR 作者 zjd0112 合并时间 2026-05-21 01:06 文件变更 3 提交数 3 评论 2 代码增减 +9 / -0

执行摘要

修复 Mamba LRU 链表节点引用循环内存泄漏

修复 scheduler RSS 在混合 Mamba 模型(如 Qwen3-Next)持续负载下线性增长的内存泄漏,实测约为 3.7 GB/h。作者通过 malloc_trim、flush_cache、gc.collect 等诊断手段定位到 gen-2 GC 可回收约 1.9 GB,确认是引用循环导致。

建议合并。该 PR 定位精准、修复简洁、验证充分,属于典型的高收益低风险修复。值得学习的是其系统性的诊断方法(malloc_trim、gc.collect、DEBUG_SAVEALL)和 reviewer 提醒的统一修复范围。

讨论亮点

审核者 hzh0425 在 Issue 评论中指出 swa_radix_cacheUnifiedRadixCache 存在相同的 _remove_node bug 模式,建议一并修复。作者 zjd0112 在下一个 commit 中同步修复了这两个文件,获得审核者批准。

实现拆解

  1. python/sglang/srt/mem_cache/mamba_radix_cache.pyLRUList._remove_node 中,在原有的邻居指针更新后,增加 setattr(node, self.prv, None)setattr(node, self.nxt, None),清除被移除节点的前后向引用。
  2. python/sglang/srt/mem_cache/swa_radix_cache.pySWALRUList._remove_node 中,应用完全相同的修复。
  3. python/sglang/srt/mem_cache/unified_radix_cache.pyUnifiedLRUList._remove_node 中,应用类似修复,但使用直接属性赋值 node.lru_prev[pt] = None; node.lru_next[pt] = None
  4. 所有修复仅增加 2 行代码,无其他逻辑变更,不改动外部接口或行为。
文件 模块 状态 重要度
python/sglang/srt/mem_cache/mamba_radix_cache.py 内存缓存 modified 4.7
python/sglang/srt/mem_cache/swa_radix_cache.py 内存缓存 modified 4.7
python/sglang/srt/mem_cache/unified_radix_cache.py 内存缓存 modified 4.7

关键符号

_remove_node

关键源码片段

python/sglang/srt/mem_cache/mamba_radix_cache.py core-logic

原始 bug 所在文件,LRUList._remove_node 方法中清除 node.prev/node.next 指针以打破引用循环。

def _remove_node(self, node):
    """Helper to remove node from linked list"""
    # 更新邻居指针跳过被删除节点
    setattr(getattr(node, self.prv), self.nxt, getattr(node, self.nxt)) # node.prev.next = node.next
    setattr(getattr(node, self.nxt), self.prv, getattr(node, self.prv)) # node.next.prev = node.prev
    # 清除自指针,打断被逐出节点间的引用循环(关键修复)
    setattr(node, self.prv, None)
    setattr(node, self.nxt, None)
python/sglang/srt/mem_cache/swa_radix_cache.py core-logic

SWALRUList._remove_node 存在完全相同的 bug,同步修复。

def _remove_node(self, node):
    """Helper to remove node from linked list"""
    setattr(getattr(node, self.prv), self.nxt, getattr(node, self.nxt)) # node.prev.next = node.next
    setattr(getattr(node, self.nxt), self.prv, getattr(node, self.prv)) # node.next.prev = node.prev
    # 清除自指针,打破引用循环
    setattr(node, self.prv, None)
    setattr(node, self.nxt, None)
python/sglang/srt/mem_cache/unified_radix_cache.py core-logic

UnifiedLRUList._remove_node 存在完全相同的 bug,同步修复。

def _remove_node(self, node: UnifiedTreeNode):
    pt = self._pt
    # 从链表中摘除当前节点
    node.lru_prev[pt].lru_next[pt] = node.lru_next[pt]
    node.lru_next[pt].lru_prev[pt] = node.lru_prev[pt]
    # 清除自指针,打断引用循环
    node.lru_prev[pt] = None
    node.lru_next[pt] = None

评论区精华

是否应将修复扩展到 swa 和 unified radix cache 正确性

hzh0425 在 Issue 评论中建议:"Could you also fix this for swa radix tree and UnifiedRadixTree?"

结论:作者同意并在后续 commit 中同步修复了这两个文件,确保所有 LRU 链表实现一致。 · 已解决

风险与影响

风险极低:仅 3 个文件各增加 2 行清零操作,不改变控制流或外部行为。清零后节点若被意外复用(dangling pointer)可能导致断言失败,但现有代码中节点被移除后不会再访问其 LRU 指针(逻辑由调用方保证),因此回归风险很小。

直接影响:修复在混合 Mamba 模型持续推理时 scheduler 的内存泄漏,长期运行可避免 OOM 或 RSS 爆炸。间接影响:所有使用 LRU 链表缓存的模块(mamba、swa、unified radix tree)都将受益于更及时的内存回收。对单次请求无行为变化。

核心路径变更 缺少测试覆盖

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论