共计 2927 个字符,预计需要花费 8 分钟才能阅读完成。
Chrome 的多过程架构
过程 & 线程
在谈浏览器多过程架构之前,咱们先聊聊过程和线程的概念。
过程是零碎进行资源调度和调配的的根本单位,一个过程能够认为是一个程序的运行实例。启动一个程序的时候,操作系统会为该程序创立一块内存,用来寄存代码、运行中的数据和一个执行工作的主线程,咱们把这样的一个运行环境叫过程。
线程是依附于过程的,是操作系统可辨认的最小执行和调度单位,而过程中应用多线程并行处理能晋升运算效率,多线程能够并行处理工作,然而线程是不能独自存在的,它是由过程来启动和治理的。咱们最相熟的 JS 的运行就是线程这个维度。
上面是过程和线程间的一些区别:
- 一个线程依附于一个过程,一个过程能够有多个线程;
- 线程之间共享过程中的数据。
- 过程中的任意一线程执行出错,都会导致整个过程的解体,而过程之前不会相互影响。
- 当一个过程敞开之后,操作系统会回收过程所占用的内存。
- 过程之间的内容互相隔离,只能通过 IPC 通信。
我感觉知乎上一个用火车比喻过程和线程的例子比拟形象:过程好比火车,线程好比车厢,一辆火车有多节车厢,不同的火车之间互不烦扰,然而一节车厢失火会殃及多节车厢。
Chrome 架构
Chrome 浏览器包含:1 个 Browser 主过程、1 个 GPU 过程、1 个 Utility 过程、多个 Renderer 过程和多个 Plugin 过程。
- Browser 主过程(浏览器过程):次要负责界面显示、用户交互、子过程治理,同时还蕴含网络申请和文件拜访等。
- GPU 过程:与其余过程隔离解决 GPU 工作。
- Renderer 过程(渲染过程):外围工作是将 HTML、CSS 和 JavaScript 转换为用户能够与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该过程中,默认状况下,Chrome 会为每个 Tab 标签创立一个渲染过程。出于平安思考,渲染过程都是运行在沙箱模式下。
- Plugin 过程:次要是负责插件的运行,因插件易解体,所以须要通过插件过程来隔离,以保障插件过程解体不会对浏览器和页面造成影响。
浏览器渲染机制
渲染过程的外围工作是将 HTML、CSS 和 JavaScript 转换为用户能够与之交互的网页。在这个工作过程中,输出的 HTML 通过一些子阶段,最初输入像素。依照渲染的工夫程序,这些子阶段大抵可分为:构建 DOM 树、计算款式、布局、分层、绘制、分块、栅格化和合成。
构建 DOM 树
当渲染过程开始接管 HTML 数据时,主线程开始解析 HTML 并将其转换为浏览器可能了解的 DOM 树结构。
在 DOM 树的解析过程中,如果遇到 img、css 或者 js 资源时,主线程会向 Browser 主过程的网络线程发送申请以获取对应的资源。
当解析的过程中遇到 <script>
标签时,主线程会暂停 HTML 的解析,从而进行 js 代码的加载、解析和执行。因为 js 代码中可能波及对页面构造的批改,主线程必须期待 js 运行能力复原对 HTML 文档的解析。因而咱们能够通过在 <script>
标签上加上 async 或者 defer 属性来异步加载执行 js 代码,防止 js 阻塞 HTML 的解析。
款式计算
款式计算的目标是为了计算出 DOM 节点中每个元素的具体款式,在计算过程中须要恪守 CSS 的继承和层叠两个规定,这个阶段大体可分为三步来实现:
- 把 CSS 转换为浏览器可能了解的构造:当渲染引擎接管到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器能够了解的构造——StyleSheets;
- 转换样式表中的属性值,使其标准化:例如 rem 这些属性,须要将所有值转换为渲染引擎容易了解的、标准化的计算值;
- 计算出 DOM 树中每个节点的具体款式
在浏览器中,咱们能够通过 Computed 面板查看以后节点的 Computed Style。
布局
有了 DOM 树和 DOM 对应的 Computed Style 之后还不足以显示页面,接下来还须要计算出 DOM 树中可见元素的几何地位,这个计算过程叫做布局。
Chrome 在布局阶段须要实现两个工作:创立布局树和布局计算。
- 创立布局树:浏览器会遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中,而不可见的节点会被布局树疏忽掉,如图中 span 这个元素被设置为 dispaly:none,这个元素会被布局树疏忽;
- 布局计算:有了布局树后,浏览器会计算布局节点的坐标地位;
分层
有了布局树后,对于一些简略页面曾经具备绘制条件了,然而对于咱们古代的页面来说,有很多简单的成果,如一些简单的 3D 转换、z-index 做 z 轴排序等,对于这些场景为了页面展现的正确性,渲染引擎还会为特定的节点生成专用的图层,并生成一棵对应的图层树。
须要留神的是,并不是每个节点都蕴含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点所在的图层。最终每一个节点都会间接或者间接地从属于一个图层。
通常满足上面两点便会晋升为一个独自的图层:
- 领有层叠上下文属性的元素会被晋升为独自的一层。层叠上下文
- 须要剪裁的中央也会被创立为图层(overflow)。
咱们能够在浏览器的 layers 面板看到以后页面的分层状况:
绘制
在确定了 DOM 树、计算款式以及布局树依然不足以绘制页面,这里还须要有明确的绘制程序,在此过程中主线程会遍历布局树并创立绘制记录。
同样,咱们能够在浏览器的 layers 面板看到当前页面对应图层的绘制记录:
光栅化(栅格化)
在确定了布局树并创立了图层以及对应的绘制程序之后,主线程会将信息提交给合成线程。合成线程会去光栅化每一个图层。
因为咱们浏览器的视口是无限的,然而页面的长度可能很长,有些图层可能超过视口很多,而用户对页面的感知是视口维度的,一次性渲染整个图层未免有些节约,因而合成线程会对图层进行 分块解决。
有了图块之后,合成线程会将每一个图块发送到光栅线程(Raster thread),光栅线程会光栅化每一个图块并存在 GPU 内存中。在这个过程中,合成线程会优先选择视口内的图块提交给光栅线程。
合成和展现
光栅化实现后,合成线程会创立合成帧通过 IPC 通信提交给浏览器过程。浏览器过程接管到指令后会将内容绘制在内存中并展现在屏幕上。
至此,从接管 HTML 数据到页面的展现全流程就完结了。上面咱们再联合浏览器的渲染流程看下什么是重排、重绘和合成。
重排、重绘和间接合成
重排
当咱们通过 js 或者 css 属性更新了元素的几何属性,例如元素的宽度、高度,此时浏览器会从新触发布局并从新执行前面全副的渲染流程,因而,重排的开销是最大的。
重绘
当咱们通过 js 或者 css 更新元素的绘制属性,例如元素的背景色、文字的色彩等,此时布局和分层阶段被省略,只执行后续的流程,因而重绘的开销相比重排会小很多。
间接合成
为什么咱们为了防止重排和重绘而去采纳 css3 的 transform 等属性呢?因为此时整个主线程的流程会被全副跳过,执行后续的流程,而后续的流程交给了在执行线程、光栅线程和 GPU 过程上执行没有占据主线程的资源,因而效率是最高的。
参考文章
- https://developer.chrome.com/…
- https://developer.chrome.com/…
- https://developer.mozilla.org…
- https://www.zhihu.com/questio…