浏览器进程和线程

参考:https://www.jianshu.com/p/c1808d0c1d45

1. 浏览器进程

图片说明

浏览器进程

1.1 浏览器进程 main

浏览器的主进程,负责协调、主控,只有一个(无论打开几个tab或几个弹窗),主要作用:

  • 负责浏览器界面显示,与用户交互,如前进,后退等;
  • 负责各个页面的管理,创建和销毁其他进程;
  • 将 Renderer 进程得到的内存中的 Bitmap,绘制到用户界面上;
  • 网络资源的管理,下载等;

1.2 CPU进程

用于 3D 绘制等,可禁用掉,只有一个

1.3 第三方插件进程

每种类型的插件对应一个进程,仅当使用该插件时才创建。

1.4 浏览器渲染进程

浏览器渲染进程(Renderer 进程),即通常所说的浏览器内核,主要作用:页面渲染、脚本执行、事件处理等。每一个标签页的打开都会创建一个Renderer 进程,且互不影响。默认一个标签页一个 Renderer 进程,但是,有时候浏览器会将多个进程合并(暂时没查到合并的依据),比如打开多个空白标签页。

作为前端开发,最关心的自然是页面渲染、脚本执行、事件处理等过程,这就不得不介绍 Renderer 进程。

首先,Renderer 进程包含以下5种线程:

1.4.1 GUI渲染线程

  • 负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等;

  • 当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行;

    注意:GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起(相当于被冻结了),GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。

1.4.2 js 引擎线程

  • JS 引擎线程也称为 JS 内核,负责处理 Javascript 脚本程序,解析 Javascript 脚本,运行代码;
  • JS 引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个 JS 线程在运行 JS 程序

注意:GUI 渲染线程与 JS 引擎线程的互斥关系,所以如果 JS 执行的时间过长,会造成页面的渲染不连贯,导致页面渲染加载阻塞。

实例:

function demo() {
    const now = Date.now();
    document.body.style.backgroundColor = 'red';
    while(Date.now() - now <= 2000) { continue; }
    document.body.style.backgroundColor = 'blue';
}

正确答案:2s后直接变蓝。

解释:Js 线程执行到 document.body.style.backgroundColor = 'red';,Js 线程知道这个是 GUI 线程该做的事,于是把该任务放到 GUI 线程中的任务队列里(并未执行),然后 Js 线程直到while循环,在这忙等了 2s。然后碰到 document.body.style.backgroundColor = 'blue'; GUI 线程把它压到任务队列里(并未执行)。此时 Js执行已经完毕,于是 GUI 线程执行,清空 GUI 线程的任务,最后一个任务是变蓝,于是是 2s 后直接变蓝。

1.4.3 事件触发线程

  • 归属于浏览器而不是 JS 引擎,用来控制事件循环(可以理解,JS 引擎自己都忙不过来,需要浏览器另开线程协助);

  • 当 JS 引擎执行代码块如 setTimeOut 时(也可是来自浏览器内核的其他线程,如鼠标点击、AJAX 异步请求等),会将对应任务添加到事件触发线程中;

  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待 JS 引擎的处理;

    注意:由于 JS 的单线程关系,所以这些待处理队列中的事件都得排队等待 JS 引擎处理(当 JS 引擎空闲时才会去执行);

1.4.4 定时器触发线程

  • 即 setInterval 与 setTimeout 所在线程;

  • 浏览器定时计数器并不是由 JS 引擎计数的,因为 JS 引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性;

  • 因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待 JS 引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行定时器只是在指定时间点将任务添加到事件队列中

    注意:W3C 在 HTML 标准中规定,定时器的定时时间不能小于 4ms,如果是小于 4ms,则默认为4ms。

1.4.5 异步HTTP请求线程

  • XMLHttpRequest 连接后通过浏览器新开一个线程请求;

  • 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待 JS 引擎空闲后执行;

综上所述,浏览器各进程线程关系大致如下图:

图片说明

浏览器进程线程关系图

2. 浏览器为什么是多进程

对于简单的网页,一个进程处理多个网页是可行的。

但是把很多复杂的网页放进一个进程,浏览器面临在健壮性,响应速度,安全性方面的挑战。如果一个tab页崩溃,将导致同进程的其他页面崩溃,极其影响用户体验。

另外进程之间是不共享资源和地址空间的,所以不会存在太多的安全问题。

当然,多进程相对于单进程,内存等资源消耗更大,有点空间换时间的意思。这大概也是浏览器中存在多个tab页共用一个进程的情况的原因吧。