5.4 KiB
5.4 KiB
内存优化分析与修复方案
问题分析
基于 /sw-debug.html 的内存日志分析,发现以下问题:
内存使用情况
| 指标 | 值 | 问题 |
|---|---|---|
| 已使用内存 | 3.3-4.3 GB | 🔴 极高 |
| 内存上限 | 4 GB | 接近/超过上限 |
| 使用率 | 77-101% | 🔴 危险区域 |
| 图片数量 | 30 | 可能过多 |
| 视频数量 | 1 | - |
| Plait 元素 | 80 | 正常 |
关键发现
- 启动时就有 ~3.5GB 内存 - 说明不是运行时泄漏,而是初始加载问题
- 长任务期间内存飙升到 4.3GB - 超过了 4GB 限制
- 刷新后内存不降反升 - 每次刷新内存都在增长
问题根源
1. 高分辨率图片解码 (主要问题)
问题描述:
- 30 张图片在画布上全部解码到内存中
- 一张 4K 图片 (4096x2160) 解码后约 35MB
- 30 张高分辨率图片可能占用 300-1000MB
代码位置:
packages/drawnix/src/plugins/with-image.tsx- Plait 框架内部的图片渲染
2. 聊天附件 Base64 存储 (中等问题)
问题描述:
- 聊天消息的附件以 base64 字符串形式存储在 IndexedDB
- 一张 2MB 的图片转为 base64 后约 2.6MB
- 大量附件累积会占用大量存储和内存
代码位置:
// packages/drawnix/src/types/chat.types.ts
export interface Attachment {
data: string; // Base64 编码,可能非常大
}
3. 任务数据累积
问题描述:
- 已完成的任务永久保存在 IndexedDB
- 任务结果包含 URL 引用,但工作流数据可能较大
代码位置:
apps/web/src/sw/task-queue/storage.ts
4. 视频解码缓冲区
问题描述:
- 视频元素的解码缓冲区占用内存
- 即使视频暂停,缓冲区也可能保留
已实施的修复
1. 内存监控服务
添加了 memory-monitor-service.ts:
// packages/drawnix/src/services/memory-monitor-service.ts
// 功能:
// - 周期性检查内存使用(每 30 秒)
// - 在内存压力过大时触发清理(75% 阈值)
// - 支持注册自定义清理处理器
// - 提供内存统计 API
// 使用:
import { memoryMonitorService } from './services/memory-monitor-service';
// 获取内存状态
const stats = memoryMonitorService.getMemoryStats();
console.log(stats?.formatted.used); // "3.2 GB"
// 手动触发清理
await memoryMonitorService.triggerCleanup();
2. 自动启动内存监控
在 apps/web/src/main.tsx 中添加了自动启动:
import { memoryMonitorService } from '../../../packages/drawnix/src/services/memory-monitor-service';
// 延迟 5 秒启动,避免影响首屏加载
setTimeout(() => {
memoryMonitorService.start();
memoryMonitorService.logMemoryStatus();
}, 5000);
3. 离屏图片自动卸载
内存监控服务包含了离屏图片清理逻辑:
// 找到离屏图片并暂时移除 src
const isOffscreen =
rect.bottom < -500 ||
rect.top > window.innerHeight + 500;
if (isOffscreen) {
img.setAttribute('data-lazy-src', src);
img.removeAttribute('src');
}
建议的进一步优化
优先级 1:图片懒加载优化
目标:只解码视口内的图片
方案:
- 在 Plait 的图片渲染中使用
loading="lazy"和decoding="async" - 对离屏图片使用占位符而不是实际图片
修改位置:
packages/drawnix/src/plugins/with-image.tsx
优先级 2:聊天附件外部化存储
目标:减少 IndexedDB 中的 base64 数据
方案:
- 将附件 Blob 存储到 Cache API
- 在 IndexedDB 中只存储虚拟 URL 引用
- 需要时从 Cache API 获取
修改位置:
packages/drawnix/src/services/chat-storage-service.tspackages/drawnix/src/types/chat.types.ts
优先级 3:图片尺寸限制
目标:限制画布上图片的最大解码尺寸
方案:
- 在插入图片时检查尺寸
- 对超大图片自动缩放
- 提供高清预览选项
优先级 4:定期内存报告
目标:帮助用户了解内存使用情况
方案:
- 在设置页面显示内存使用统计
- 在内存压力时显示警告
- 提供手动清理选项
用户操作建议
如果遇到内存问题,可以:
- 减少画布上的图片数量 - 将不需要的图片移到其他画板
- 使用较低分辨率的图片 - 特别是在移动设备上
- 定期刷新页面 - 释放累积的内存
- 清理聊天历史 - 删除包含大量附件的旧会话
- 关闭其他标签页 - 释放浏览器资源
调试方法
使用 SW Debug 面板
访问 /sw-debug.html 查看:
- 内存使用趋势
- 长任务监控
- 内存快照
使用 Chrome DevTools
-
Memory 面板:
- 拍摄堆快照分析内存分布
- 查看哪些对象占用最多内存
-
Performance 面板:
- 记录性能profile
- 分析长任务和内存增长
控制台命令
// 获取内存状态
memoryMonitorService.logMemoryStatus();
// 手动触发清理
await memoryMonitorService.triggerCleanup(true);
// 获取详细统计
const stats = memoryMonitorService.getMemoryStats();
console.table(stats);
相关文件
packages/drawnix/src/services/memory-monitor-service.ts- 内存监控服务apps/web/src/main.tsx- 内存监控初始化packages/drawnix/src/components/lazy-image/LazyImage.tsx- 懒加载图片组件packages/drawnix/src/hooks/useImageLazyLoad.ts- 图片懒加载 Hook