Skip to content

前端每收到一个流式事件就重渲染一次整个界面,思考输出太快时渲染进程内存耗尽 #158

Description

@ChHsiching

现象

生成进行到一半,浏览器标签页的渲染进程内存耗尽,整页变成「喔唷,崩溃啦!」错误页,错误代码 Out of Memory,也就是 Chrome 官方帮助里的 Aw, Snap 错误。SSE 连接跟着断开。服务端检测到连接断开后会杀掉 CLI 子进程,并且有意不发结束事件,于是任务一直显示「运行中」,看不到任何报错。

复现信息

运行方式是 pnpm dev,在页面上点「生成 HTML」按钮,或按 Ctrl+Enter。

Agent 用的是 ZCode 适配器,目前在分支上,还没合入 main。下面「问题定位」一节会说明这个 bug 跟具体哪个适配器无关。模型是 DeepSeek flash,思考模式开到 low 档,模板是 deck 类。

服务端的证据来自临时加在 SSE 发送路径上的按事件类型计数的日志,测完删掉。一次 115 秒的回合里,meta 事件共 6,397 条、426 KB,最后 20 秒冲到高峰,每秒 240 多条。同一回合的 HTML delta 全程只有 26 条、1.4 KB。崩溃就发生在这 20 秒里。

崩溃后页面上的表现:Stats 面板 TTFB 是 —、Size 0.0 KB,任务一直显示运行中。

根因

前端每收到一个流式事件就更新一次 store,PreviewPane 和日志面板跟着把整个界面重渲染一遍。日志数组本身在 store.ts 里有 slice(-400) 的 400 条上限,所以问题不在数组无限变长,而在每个事件都要重渲染一遍整个列表,总开销是事件条数乘以列表长度。事件来得比渲染快时,处理就落后于到达,没处理完的事件在连接缓冲和更新队列里越积越多,直到把渲染进程的内存撑爆。

思考模式开启时,模型把思考内容按 token 级的小帧往外推。DeepSeek 在 flash 档的吞吐下实测高峰达到每秒 240 多条 meta 事件,超过了前端处理得过来的速度。

问题定位:离线回放事件流,不经过任何 agent 和 provider

在 /api/convert 临时加了一段代码,把一串事件按设定好的速度直接发给前端,在原版 headless Chromium 里用 Playwright 监测渲染进程。没有逐帧的原始流量可以回放,事件按实测数据构造:总数 6,397 条 meta、424 KB、115 秒,速度照抄复现信息里 DeepSeek 崩溃那次的计数日志,先每秒 94 条持续 15 秒,再每秒 247 条持续 20 秒。

回放完全复现了症状。高峰期主线程完全占满,连查询页面状态都要等 4 秒以上,内存从 33 MB 涨到 87 MB,回放结束后还有 31.8 MB 没释放。这一轮没有任何 agent 和模型参与,光靠事件流就能触发同样的故障;原版 Chromium 只是严重卡顿,没有崩,真实崩溃发生在内存基线更高的浏览器里。

作为对照,又按 GLM 正常运行时的速度回放了一轮。实际使用里 GLM 默认档全程正常完成,8,421 条 thinking 日志摊在 230 秒里,约每秒 36 条;按这个速度发,主线程占用 92%,但处理一条只要 26.6 毫秒,两条事件间隔 27.7 毫秒,刚好来得及,正常跑完,内存回落。回放和实际使用的结果一致。实际使用里的其他对照也对得上:关掉思考模式就没有思考流,正常完成;DeepSeek 三种 API 端点每帧都一样小,全部崩。

结论:把事件条数变成等量渲染开销的机制在前端。模型推帧的快慢决定一次运行会不会超过前端的能力,浏览器的内存预算决定落后之后多久崩溃。这两个都不是原因本身。claude 适配器的 thinking_delta 在 argv.ts 里同样映射成 meta 事件,所以这个 bug 不依赖任何特定适配器合入 main。

修复方案

  • 在 use-convert.ts 的事件接收处加一层缓冲:事件先攒着,每 100 毫秒一次性写进 store。每秒渲染次数从最多 240 次降到最多 10 次,事件来得再快也只是排队,不会压垮渲染。
  • 第一段 HTML 立即写入,TTFB 不变;done 和 error 立即写入不缓冲;回合结束、异常或取消时把缓冲里剩的全部写入,不丢事件。
  • store 和组件都不动,不加新依赖,仍然用 SSE。
  • 连接断开时收不到结束事件,现在的代码会把任务当成完成。一并修:收不到结束事件就写一条连接中断的错误日志,任务转错误态。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinghelp wantedExtra attention is needed

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions