Tab Discard

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资源的使用,如下图所示:

Tab Discard

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

目前Chromium 81已经实现了Lifecycle一部分,打开chrome://discards/,可以看到:

Chrome Discards

目前有3个最重要的动作:

默认的页面,只有处于不显示状态才能执行Freeze或者Discard动作,只能从Discard状态执行Load操作。

Tab Discard

Chromium实现Tab Discard的功能核心类是在TabManager,它复负责触发tab lifecycle状态的转换。TabManager中有个MemoryPressureListener成员,当收到MEMORY_PRESSURE_LEVEL_CRITICAL事件时,会自动触发Discard Tab动作。

此外,我们也可以打开chrome://discards/,去体验Lifecycle

TabLifecycleUnit对象在Lifecycle中表示一个tab,实际上的操作都是发生在这个类。目前Discard实现是:

  1. 根据原来的WebContents上下文信息,新创建一个kNoRendererProcess,即不需要对应render进程的WebContents
  2. 关闭原来的WebContents的渲染进程
  3. 用新创建的WebContents在tab_strip中替代原来的WebContents

Freeze的是实现是直接调用WebContents的SetPageFrozen接口,最终会调用到blink中webview的SetPageFrozen。目前blink中webview响应Freeze会暂停页面中的音视频播放。

Load实现很简单,调用WebContents的NavigationController重新加载一次页面。