Files
TrueGrowth/docs/MEMORY_OPTIMIZATION.md

5.4 KiB
Raw Blame History

内存优化分析与修复方案

问题分析

基于 /sw-debug.html 的内存日志分析,发现以下问题:

内存使用情况

指标 问题
已使用内存 3.3-4.3 GB 🔴 极高
内存上限 4 GB 接近/超过上限
使用率 77-101% 🔴 危险区域
图片数量 30 可能过多
视频数量 1 -
Plait 元素 80 正常

关键发现

  1. 启动时就有 ~3.5GB 内存 - 说明不是运行时泄漏,而是初始加载问题
  2. 长任务期间内存飙升到 4.3GB - 超过了 4GB 限制
  3. 刷新后内存不降反升 - 每次刷新内存都在增长

问题根源

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图片懒加载优化

目标:只解码视口内的图片

方案

  1. 在 Plait 的图片渲染中使用 loading="lazy"decoding="async"
  2. 对离屏图片使用占位符而不是实际图片

修改位置

  • packages/drawnix/src/plugins/with-image.tsx

优先级 2聊天附件外部化存储

目标:减少 IndexedDB 中的 base64 数据

方案

  1. 将附件 Blob 存储到 Cache API
  2. 在 IndexedDB 中只存储虚拟 URL 引用
  3. 需要时从 Cache API 获取

修改位置

  • packages/drawnix/src/services/chat-storage-service.ts
  • packages/drawnix/src/types/chat.types.ts

优先级 3图片尺寸限制

目标:限制画布上图片的最大解码尺寸

方案

  1. 在插入图片时检查尺寸
  2. 对超大图片自动缩放
  3. 提供高清预览选项

优先级 4定期内存报告

目标:帮助用户了解内存使用情况

方案

  1. 在设置页面显示内存使用统计
  2. 在内存压力时显示警告
  3. 提供手动清理选项

用户操作建议

如果遇到内存问题,可以:

  1. 减少画布上的图片数量 - 将不需要的图片移到其他画板
  2. 使用较低分辨率的图片 - 特别是在移动设备上
  3. 定期刷新页面 - 释放累积的内存
  4. 清理聊天历史 - 删除包含大量附件的旧会话
  5. 关闭其他标签页 - 释放浏览器资源

调试方法

使用 SW Debug 面板

访问 /sw-debug.html 查看:

  • 内存使用趋势
  • 长任务监控
  • 内存快照

使用 Chrome DevTools

  1. Memory 面板

    • 拍摄堆快照分析内存分布
    • 查看哪些对象占用最多内存
  2. 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