2015年Chrome浏览器推出了一个新的实验性特性Tab Discard,用来减少浏览器使用的内存。其具体的原理的是当浏览器某个tab处于不显示的状态时,保存该tab少许的上下文信息,然后把该tab网页对应的渲染进程给销毁。当它处于显示时,又重新加载起来。
Tab Discard看起来是很简单的策略,但是确实能够有效的减少浏览器的内存使用。平均每个页面的渲染进程差不多会占用50MB的内存,如果Tab Discard多个页面,能够节省的内存总量是很可观的。另外说一下,新的Chromium的进程模型已不再遵循之前的 Process Models 。大概从Chromium 67开始,新的 Site Isolation 安全策略导致几乎每打开一个页面都会起一个新的进程。所以新版本的Chromium看起来占用的内存越来越多。
更进一步,参考手机移动操作系统的app生命周期,Chromium开发者也提出了一套用于web页面的Lifecycle API
,使得浏览器可以更智能的调度内存和CPU资源的使用,如下图所示:

但目前web开发还普遍没有这种页面Lifecycle的意识。会出现页面被discard了,在编辑框里面的输入文字在重新加载之后会丢失的这种情况。因此有配套的api,通知web页面各种Lifecycle事件和在后台工作的能力。具体的标准可以参考 Page Lifecycle 草案。
目前Chromium 81已经实现了Lifecycle
一部分,打开chrome://discards/
,可以看到:

目前有3个最重要的动作:
- Load,重新加载页面
- Freeze,冻结页面,使其不再占用CPU
- Discard,丢弃页面,使其不再占用CPU和内存
默认的页面,只有处于不显示状态才能执行Freeze或者Discard动作,只能从Discard状态执行Load操作。

Chromium实现Tab Discard的功能核心类是在TabManager,它复负责触发tab lifecycle状态的转换。TabManager中有个MemoryPressureListener成员,当收到MEMORY_PRESSURE_LEVEL_CRITICAL事件时,会自动触发Discard Tab动作。
此外,我们也可以打开chrome://discards/
,去体验Lifecycle
。
TabLifecycleUnit对象在Lifecycle
中表示一个tab,实际上的操作都是发生在这个类。目前Discard实现是:
- 根据原来的WebContents上下文信息,新创建一个kNoRendererProcess,即不需要对应render进程的WebContents
- 关闭原来的WebContents的渲染进程
- 用新创建的WebContents在tab_strip中替代原来的WebContents
Freeze的是实现是直接调用WebContents的SetPageFrozen接口,最终会调用到blink中webview的SetPageFrozen。目前blink中webview响应Freeze会暂停页面中的音视频播放。
Load实现很简单,调用WebContents的NavigationController重新加载一次页面。