rxjs长效流若未用takeuntil或unsubscribe终止,会导致内存持续累积、组件无法回收、ui迟钝甚至崩溃;典型场景包括未清理的dom事件流、失控的interval定时器、subject广播残留订阅及switchmap组合后未终止的http流。

如果不善用 takeUntil 或主动 unsubscribe,RxJS 中的长效流(infinite observables)会持续运行、持有引用,最终导致内存无法释放——这不是“偶尔卡顿”,而是资源缓慢累积、组件无法回收、UI 响应变迟钝甚至崩溃。
DOM 事件流长期驻留
像 fromEvent(document, 'scroll')、fromEvent(input, 'input') 这类流不会自动结束。若订阅后没清理,哪怕组件已销毁,事件监听器仍挂在 DOM 上,同时回调闭包中捕获的组件实例(如 this)、状态变量、服务引用全被锁住。
- 滚动监听器持续触发,执行逻辑却在已卸载的组件里运行
- 输入框防抖流(debounceTime + switchMap)不断新建内部订阅,旧订阅未取消,HTTP 请求可能发出去但结果无人处理
- 浏览器开发者工具中 Memory 面板可见 detached DOM 节点数量持续上升
定时类流(interval、timer)失控增长
interval(1000) 或 timer(0, 2000) 是典型无限流。一旦订阅,它会永远发射值,除非显式终止。
- 组件反复创建又销毁,每个实例都启一个 interval,但没人调
subscription.unsubscribe() - 计时器回调中访问的 this.state、this.service 等保持强引用,整个组件实例无法 GC
- 在 Angular 或 React 中表现为:路由跳转多次后,控制台日志里看到“每秒多一条”重复输出
Subject/Broadcaster 类流持续广播
BehaviorSubject、ReplaySubject、Subject 本身不自动完成,它们维持内部观察者列表和缓存值。若外部长期订阅了 service 中的 subject,而没在组件销毁时退订:
- subject 的 observers 数组不断累积,每次
.next()都遍历所有“僵尸订阅者” - subject 缓存的最新值(尤其 large object)一直被 retain,GC 无法回收
- 多个组件监听同一 service subject,其中一个销毁后未退订,它仍接收并尝试更新已不存在的视图
HTTP 流虽有限但组合后变长效
单个 this.http.get() 是 finite observable,自动 complete;但一旦套进 switchMap、mergeMap 或与事件流结合,整体就变成 infinite 流。
- 搜索框 +
switchMap→ 每次输入都生成新请求流,旧流若未被 switchMap 自动取消(需确保上游流可终止),就残留为 pending subscription - 配合
retry({ count: 3 })且失败后未加takeUntil,重试逻辑会持续占用资源直到手动切断 - 后台轮询(
interval(5000).pipe(switchMap(() => api.poll())))若没绑定生命周期,页面关闭后仍在后台发请求
这些堆积不是瞬间爆发,而是随用户操作次数线性增长。小项目可能几小时才明显,大型 SPA 可能在 2–3 次路由切换后就出现内存占用翻倍、GC 频繁、CPU 占用异常升高。











