WebKit下FileReader调用引发Page Memory持续泄漏问题咨询
根本成因
这是iOS端WebKit内核(覆盖全版本Safari、所有使用WKWebView的内嵌浏览器)的已知实现缺陷:
所有对<input type="file">返回的File对象的二进制读取操作,包括FileReader全系列读方法、URL.createObjectURL生成地址后加载资源的操作,都会在内核的WebContent进程堆外内存中留存一份文件映射缓存。这部分内存完全不受JavaScript垃圾回收机制管控,前端没有任何官方接口可以主动触发清理:
- 你在JS层解除对
File对象、读取结果ArrayBuffer、Blob URL的引用,调用URL.revokeObjectURL,都不会影响这部分内核缓存 - 复用FileReader实例、移除事件监听器、解除Promise引用等JS层的内存优化操作,完全无法触达这部分内核态内存,因此不会有任何效果
当这部分缓存累计到iOS对单个WebContent进程设定的内存阈值(iPad Mini设备阈值约为350MB),系统会对进程触发内存压缩,直接表现为页面运行卡顿、IndexedDB/WebSocket等常驻连接被内核强制中断,不会直接触发页面崩溃重载。你在Safari内存工具中看到的Page Memory持续上涨,就是这部分不在JS堆统计范围内的内核缓存。
可落地的规避方案
按照改造成本从低到高排序,可根据业务场景选择:
- 前置压缩文件体积
不要直接读取原图的完整二进制数据,图片文件先通过Canvas缩放到业务要求的最大分辨率,转成压缩后的Blob对象再做读取上传。Canvas使用完成后主动将宽高设置为0,解除位图引用避免额外内存占用。该方案可以将单文件对应的缓存体积降低70%以上,大幅延长触发内存阈值的时间。 - 分块读取绕过长期缓存逻辑
调用file.slice()将大文件切分为1MB以内的小块,逐块调用FileReader读取,每块读取完成后立刻将结果拷贝到业务维护的缓冲区,解除对分块读取结果的引用。WebKit对小尺寸临时读操作不会生成长期缓存,实测该方案可减少80%以上的内核缓存留存。 - 内存压力触发缓存清理
累计读取文件体积超过100MB时,主动创建一个64MB左右的临时ArrayBuffer,保持引用2-3秒后解除引用触发JS垃圾回收。部分iOS版本在检测到进程内存压力时,会主动清理历史留存的文件映射缓存,该方案兼容性随系统版本变化,不能保证全量生效。 - iframe进程隔离(100%生效方案)
将文件读取、上传的全量逻辑放到独立的同源iframe中运行,累计读取文件体积达到阈值(比如200MB)或者单次批量上传完成后,主动将iframe从DOM中移除并解除所有引用。iOS系统会在iframe被销毁后立刻回收该iframe对应的独立WebContent进程的所有内存,包括内核留存的文件缓存,完全不会影响主应用的运行状态,适合需要长期后台运行、不能刷新主页面的离线应用场景。
注意:SPA场景下通过路由重载、刷新location的方式无法触发WebContent进程回收,因此不能释放这部分内核缓存,只有销毁独立子进程(关闭标签页、移除iframe)才能彻底清理。
内容的提问来源于stack exchange,提问作者ldwii
相关产品推荐
相关产品推荐

