浏览器中的进程与线程

浏览器中的进程与线程

进程与线程

进程

进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序的载体。进程拥有系统分配的独立的内存空间,进程之间相互独立。任一时刻,CPU总是运行一个进程,其它进程处于非运行状态。
如果把CPU比作一座时刻在运行的工厂的话,进程就相当于工厂里的车间,并且这个车间开工的时候,其它车间都必须停工。

线程

线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是CPU调度的最小单位。一个进程有一个或多个线程组成,同一进程下的各个线程之间共享程序的内存空间。
类似于上面的比喻,线程就相当于车间里的工人,车间的空间是工人们共享的。车间里的任务可由多个工人协同完成。

进程与线程的区别与联系

  • 进程是操作系统分配资源的最小单位,线程是程序执行的最小单位。
  • 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间以及进程级资源
  • 进程间切换的代价要比线程间切换的代价大得多

多进程与多线程

简单的来说,多进程就是在某一时刻,计算机系统中允许两个或两个以上的进程处于运行状态,而多线程就是在一个程序中可以运行多个不同的并行执行线程来执行不同的任务。

浏览器中的进程

概念

打开 chrome 浏览器左上角更多工具里面的任务管理器可以看到许多进程,如下

process in browser

可以看到一个浏览器主进程,GPU进程以及很多个tab标签页进程,每个进程系统都为其分配了资源(CPU,内存,网络等)。从图中还可以看出第六个进程下面有四个标签页,且这四个标签页中的第二个是由第一个标签页中的链接开的,同理第三个是由第二个标签页中的链接打开的。这和在同一个标签内实现跳转是一样的,浏览器都只为其新开一个进程并分配资源

  1. Browser进程 - 一个浏览器打开后只有一个,浏览器的主进程,主要负责浏览器用户界面的显示与交互,各个页面的管理,创建和销毁,网络资源的管理。还有就是将Render进程得到的内存中的Bitmap绘制到用户界面上。
  2. 第三方插件进程 - 每个种类的插件对应一个进程,但只有启用时才会被创建
  3. GPU进程 - 最多只有一个,用于3D绘制
  4. Render进程 - 又称为浏览器内核或浏览器渲染进程,内部是多线程的。分别由GUI渲染线程,JS引擎线程,定时触发器线程,事件触发线程以及异步http请求线程

多进程的优势与劣势

  • 由于默认是 新开 一个 tab 页面 新建 一个进程,所以单个 tab 页面 crash 不会影响到整个浏览器
  • 同样的,某个第三方插件 crash 也不会影响到这个浏览器
  • 多进程可以充分利用现代CPU多核的优势
  • 方便利用沙盒模型隔离插件以及相关 tab 页面进程,提高浏览器的稳定性与安全性
  • 系统会为浏览器新开的进程分配内存,CPU 等资源,所以内存和 CPU 的资源消耗也会更大

浏览器内核

概要

浏览器内核是诸多浏览器进程中前端最应该关注的进程,因为页面的渲染,JS的执行,事件以及定时器等诸多线程都会在这个进程中进行。下图主要列举了一些常见浏览器的 Layout Engine(GUI渲染引擎) 和 ECMAScript Engine(JS引擎) 以及浏览器内核中的一些常驻线程

Browser Name Layout Engine ECMAScript Engine
Internet Explore 9+ Trident Chakra
Microsoft Edge EdgeHTML Chakra
FireFox Gecko SpiderMonkey
Safari WebKit JavaScriptCore
Chrome Blink V8
Opera Blink V8

main threads in render process

常驻线程

  1. GUI 渲染线程
    • 负责渲染浏览器界面,解析 html,解析 css,构建 DOM tree,构建 Style tree,构建 render tree,负责布局(Layout)以及绘制(Painting)
    • 当界面需要重绘(repaint)以及回流(reflow)时,该线程也会执行,这两个操作也是导致网站性能的关键原因
    • GUI 线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行
  2. JS 引擎线程
    • 又称 JS 内核,负责解析 JavaScript 脚本程序,运行代码
    • JS 引擎一直等待这任务队列中的任务到来,然后处理,即浏览器内核中只有一个 JS 线程在运行 JS 程序
    • 由于 JS 引擎与 GUI 渲染引擎两个线程是互斥的,所以 JS 执行脚本时间过长会引起页面渲染加载阻塞导致其渲染的不连贯
  3. 事件触发线程
    • 当一个事件如鼠标点击,键盘按下以及 AJAX 异步请求等被触发时,该线程会把该事件添加到事件队列的尾部等待 JS 引擎的处理
    • 由于 JS 引擎是单线程的,这些处于事件队列中的事件都必须排队等待 JS 引擎空闲时处理
  4. 定时器触发线程
    • setIntervalsetTimeOut 所在线程,计数器由该线程进行金属而不是由 JS 引擎完成的
    • 计时完毕后,添加到事件队列中等待 JS 引擎空闲时执行
    • 在 W3C HTML 标准中规定,要求 setTimeOut 中低于4ms的时间间隔算为4ms,不过不同的浏览器可能会有不同的最小时间设定。
  5. 异步 http 请求线程
    • XMLHttpRequest对象在建立连接后,浏览器为其新开一个线程请求,一旦检测到状态变更且设置有回调函数,异步线程就将产生状态变更的时间放到时间队列中等待 JS 引擎空闲时处理

JS引擎为何为单线程?

单线程的本质就是同一时间只能做一件事。JS 的单线程与它的用途有关,作为脚本语言,JS 的主要用途是帮助浏览器与用户互动以及操作DOM。假如 JS 同时有两个线程并同时操作一个DOM节点,一个为该节点添加内容,一个准备删除该节点,浏览器就不知道以谁为准了,而且引入加锁的问题。这也就违背了 JS 避免复杂性的设计初衷。虽然为了利用 CPU 多核的计算能力,HTML5 提出了 Web Worker的标准,允许 JS 脚本创建多个线程,但是子线程完全受主线程控制,而且不能对 DOM 进行操作,所以也并没有改变 JS 引擎为单线程的本质。
如果有特别耗时的操作可以新开一个单独的 Worker 线程进行处理
详细可参见Web Workers concepts and usage

Browser 进程与浏览器内核的通信过程

browser process and render process

  • Browser 进程收到用户请求,通过网络下载获取页面内容,将该任务内容通过 RenderHost 接口传递给 Render 进程
  • Render 进程收到请求,简单解释后交给 GUI 渲染进程开始渲染
    • GUI 渲染进程接受请求开始加载并渲染网页,并且在此过程中可能需要 Browser 进程获取资源和需要 GPU 进程帮助渲染
    • 期间也可能会有 JS 引擎线程操作 DOM,可能引起重绘(repainting)或回流(reflow)
    • Render 进程最后将结果通过 RenderHost 接口返回给 Browser 进程
  • Browser 进程接受结果并将其绘制到屏幕上

网页生成的简易过程

在浏览器进程将内容通过 RenderHost 接口传递给浏览器内核后,过程大致分为以下几个部分

web page render process

  1. 解析 html 构建 dom 树
  2. 解析 css 构建 style 树
  3. 结合 DOM 合并成 render 树
  4. 布局(layout) render 树,负责各个元素尺寸,位置的计算
  5. 绘制(painting) render 树,绘制页面信息
  6. 浏览器将各层的信息发送给 GPU,GPU会将各层合成(composite),显示在屏幕上

从上述过程中,我们可以很容易的得到以下结论

  • 通常情况下,一张网页中的DOMContentLoaded事件(当DOM加载完成,不包括样式表,图片,事件触发)要比load事件(DOM,样式表,脚本,图片都已经加载完成了)先发生
  • CSS是由单独的下载线程异步下载的,不会阻塞 DOM 树的解析,但会阻止 render 树的渲染

参考

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理
前端文摘:深入解析浏览器的幕后工作原理