Android 4.4–6.0 WebView 中 will-change 会强制分配 GPU 显存且永不回收,静态声明导致图层堆积、内存持续上涨,50 个元素即可能触发 OOM;必须动态控制,仅在动画开始时设置、双 rAF 后清除。

因为 will-change 在 Android 4.4–6.0 WebView 中会立即分配 GPU 显存且永不回收,静态声明等于批量预占内存,50 个元素就可能触发 Out of Memory 崩溃。
Android 4.4–6.0 WebView 不回收图层是根本原因
这些旧版 WebView 实现中,will-change: transform 不是“建议”,而是强制指令:浏览器一解析到该声明,立刻为元素分配独立 GPU 纹理内存、创建合成层,并且不会随动画结束或鼠标移开自动释放。哪怕元素只动一帧,图层也常驻内存直到页面卸载。
- DevTools Layers 面板里能看到图层数量持续上涨,即使滚动停止也不回落
- GPU Memory 曲线在 Performance 面板中呈直线上升趋势,而非稳定波动
- 实测中,20–30 个列表项同时匹配
.list-item { will-change: transform; },低端机 10 秒内就可能 OOM 卡死
静态写死 CSS 规则 = 主动制造内存泄漏
把 will-change 写进全局样式表(比如 .card, .item, .modal)是最危险的做法——它让所有匹配元素在 DOM 挂载时就升层,与是否真要动画完全无关。
-
.list-item { will-change: transform; }→ 一屏 50 条 = 50 个常驻图层 -
div:hover { will-change: transform; }→ hover 时才加,但离开后不清理,图层堆积 -
* { will-change: transform; }→ 全局污染,连<span>都被拉进 GPU 图层
哪些写法会让内存问题更严重
某些看似“加强优化”的写法,其实在旧安卓上纯属火上浇油:
立即学习“前端免费学习笔记(深入)”;
-
will-change: contents→ 强制整个子树升层,10 行文本可能生成 20+ 图层 -
will-change: left或will-change: width→ 这些属性无法合成,浏览器仍建图层,但纯属白占资源 - 父容器和子元素都设
will-change: transform→ 图层嵌套,合成器压力指数级上升 - 混用
contain: paint或overflow: hidden→ 图层被裁剪失效,但内存照占不误
真正安全的写法必须满足三个硬条件
动态控制不是可选项,是唯一能避开 OOM 的路径:
- 添加时机:仅在
touchstart、mouseenter或animationstart中执行el.style.willChange = 'transform' - 清除时机:必须用双
requestAnimationFrame,确保图层已稳定绘制:requestAnimationFrame(() => { requestAnimationFrame(() => { el.style.willChange = 'auto'; }); }); - 监听兜底:对 iOS Safari 或旧安卓,
transitionend可能不触发,需加setTimeout(() => el.style.willChange = 'auto', duration + 100)
最易被忽略的一点:will-change 本身不省 CPU,只换 GPU 内存;如果动画本就不卡,加它就是在给内存埋雷。


















