进程和线程
先来复习一下进程和线程的概念
- 进程是CPU资源分配的最小单位
- 进程之间相互独立
- 一个进程由一个或多个线程组成
- 线程是CPU调度的最小单位
- 多个线程相互协作完成进程的任务
- 同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)
浏览器是多进程的
浏览器之所以能够运行,是因为系统给它的进程分配了资源(CPU、内存)
可以简单理解为每打开一个Tab页面,就相当于创建了一个独立的浏览器进程,但浏览器有优化机制,有些进程可能会被合并,比如打开多个空白标签页可能会被合并为一个进程,但我试过发现并没有。。

多进程的优势
- 避免单个页面或第三方插件crash影响整个浏览器
- 多进程充分利用多核优势
- 方便使用沙盒模型隔离插件等进程,提高浏览器稳定性
当然,多线程会使内存等资源消耗更大,有空间换时间的思想
浏览器主要包含哪些进程
Browser进程
浏览器的主进程(负责协调、主控)
作用大概如下:
- 负责浏览器的界面显示,与用户交互。如前进、后退等
- 负责各个页面的管理,创建和销毁其他进程
- 将Renderer进程得到的内存中的Bitmap绘制到用户界面上
- 网络资源的下载、管理等
第三方插件进程
每种类型的插件对应一个进程,仅当使用该插件时才创建
GPU进程
最多一个,用于3D绘制等
Renderer进程(浏览器内核)
对于普通的前端操作来说,最终要的就是渲染。这是重点
该进程拥有多个线程,这些线程共同来完成页面的渲染任务。
看看都有哪些线程
GUI渲染线程
- 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等
- 当界面需要重绘或回流时,该线程就会执行
- 该线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
JS引擎线程
- 也称JS内核,负责处理JS脚本程序(如V8引擎)
- JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
事件触发线程
JS脚本的执行不会影响到html元素事件的触发,由于JS单线程关系,会将触发后需要执行的JS脚本添加到JavaScript引擎的处理队列中,当JavaScript引擎空闲时才会去执行。- 注意,该线程只是触发,触发后要执行的代码依然要放到JS引擎线程中去执行。
定时触发器线程
setTimeout和setInterval所在的线程。- 由于
JavaScript引擎是单线程的,如果处于阻塞线程状态势必会影响计时的准确性,所以浏览器中的定时器并不是由JavaScript引擎来计数的。 - 该线程只是计时,一旦计时完毕后,会将触发的脚本添加到
JavaScript引擎的处理队列中,等待JavaScript引擎空闲后再执行。 - 注意,
W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
异步HTTP请求线程
- 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
- 当状态发生变化时,如果之前有设置回调,会将这个回调再放入
JavaScript引擎的处理队列中,再由JavaScript引擎执行。
Browser进程和Renderer进程的通信过程
- Browser进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render进程
- Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染
- 渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染
- 可能会有JS线程操作DOM(这样可能会造成回流并重绘)
- 最后Render进程将结果传递给Browser进程
- Browser进程接收到结果并将结果绘制出来
Renderer进程中线程之间的关系
GUI渲染线程与JS引擎线程互斥
由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起, GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。
JS阻塞页面加载
从上述的互斥关系,可以推导出,JS如果执行时间过长就会阻塞页面。
工作者线程,JS的多线程?
Worker 接口是 Web Workers API 的一部分,指的是一种可由脚本创建的后台任务,任务执行中可以向其创建者收发信息。要创建一个 Worker ,只须调用
Worker(URL)构造函数,函数参数URL为指定的脚本。Worker 也可以创建新的 Worker,当然,所有 Worker 必须与其创建者同源(注意:Blink暂时不支持嵌套 Worker)。
需要注意的是,不是所有函数和构造函数(或者说…类)都可以在 Worker 中使用。具体参考页面 Worker 所支持的函数和类。Worker 可以使用
XMLHttpRequest发送请求,但是请求的responseXML与channel两个属性值始终返回null(fetch仍可正常使用,没有类似的限制)。
- 创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
- JS引擎线程与worker线程间通过特定的方式通信(postMessage ,需要通过序列化对象来与线程交互特定的数据)
如果有非常耗时的工作,单独开一个Worker线程,这样里面不管如何翻天覆地都不会影响JS引擎主线程, 只待计算出结果后,将结果通信给主线程即可
JS引擎是单线程的,这一点的本质仍然未改变,Worker可以理解是浏览器给JS引擎开的外挂,专门用来解决那些大量计算问题。