- Published on
技术方案沉淀:移动端 H5 音视频批量加载 OOM 优化实践
- Authors

- Name
- haobiao97
1. 背景与问题描述
在移动端 H5 页面中执行多媒体任务回显(如 handleCreateSimilar 逻辑)时,需要批量解析历史任务中的素材列表,并读取其元数据(duration 时长)。
故障现象:当素材数量较多或视频分辨率较高时,iOS Safari 及部分低端 Android 浏览器会直接触发页面崩溃(Crash)或自动刷新。
错误定性:典型的移动端内存溢出(OOM)引发的系统级强制杀进程(OOM Killer)。
2. 根因分析 (Root Cause Analysis)
移动端浏览器的单进程内存配额极为严格(通常在 500MB - 1GB 左右触发警告)。旧代码在 for...of 循环中使用了以下反模式(Anti-pattern):
- 解码缓冲区泄漏:循环调用
document.createElement('video'),JS 堆内存开销虽小,但浏览器一旦解析到src并触发preload="metadata",底层 C++ 渲染引擎会立即向系统申请分配硬件解码器缓冲区(Decoder Buffers)。 - 空间复杂度失控:假设有 n 个素材,短时间内向系统申请的底层媒体内存空间复杂度为 O(n)。
- 垃圾回收(GC)滞后:仅执行
video.src = ''只能解除 JS 层的引用,无法强迫 V8/JavaScriptCore 引擎立即触发 GC,导致底层 GPU 和媒体内存堆积,瞬间击穿系统内存水位线。
3. 核心优化策略与代码重构
为了在不破坏原有业务逻辑的前提下解决 OOM,我们从以下四个工程维度进行了重构,将空间复杂度从 O(n)降维打击至 O(1)。
3.1 引入 DOM 单例模式 (Singleton Pattern)
修改前:每次循环创建新的 <video> 或 <audio> 标签。
修改后:在函数外部或模块顶部维护全局唯一的 sharedVideoElement 和 sharedAudioElement。
原理:利用 await 的串行特性,所有素材轮流复用同一个 DOM 节点,确保同一时刻底层引擎只维护一个解码实例,将内存峰值死死压住在 O(1)。
3.2 深度内存释放 (Deep Memory Flushing)
修改前:超时或加载完成后,仅使用 video.src = ''。
修改后:引入标准化的 cleanup 清理函数:
const cleanup = () => {
video.removeAttribute('src');
video.load(); // 核心防线:强制底层引擎清空当前解码流水线与 Buffer
};