浏览器的进程模型
进程?
进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程
------百度百科
程序运行都需要自己的内存的空间,可以把这块内存空间简单的理解为进程,可以比喻为工厂
线程?
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
------百度百科
有了进程以后,就可以运行程序的代码了,运行代码的就可以称之为线程,可以比喻为工人,有了工厂以后,工厂的工人就可以干活了
浏览器有哪些进程?
浏览器是一个多进程多线程的应用程序,为了避免互相影响,减少连环崩溃的几率,启动浏览器后,它会启动多个进程
常见的浏览器进程:
-
主进程(Browser Process):也称为浏览器引擎进程或浏览器进程管理器。它是浏览器的主要控制中心,负责协调其他进程的工作。它处理用户界面、扩展管理、主要的网络请求和渲染进程的创建。
-
渲染进程(Renderer Process):每个标签页通常都有一个独立的渲染进程,负责解析和渲染网页内容、执行 JavaScript 代码以及处理用户交互。
-
网络进程(Network Process):这个进程负责处理网络请求和数据传输,与渲染进程分离,以提高安全性和稳定性,并允许并行处理多个网络请求。
-
GPU 进程(GPU Process):这是一个可选的进程,负责处理图形渲染操作。它可以卸载部分图形处理工作,以减轻渲染进程的负担,提高性能。
浏览器的进程架构可能因浏览器类型、版本和操作系统而有所不同
事件循环是发生在渲染主线程中的,所以我们接下来主要说渲染主线程
渲染主线程的工作
每个标签页通常都有一个独立的渲染进程,以保证不同的标签页之间互不影响,目前浏览器默认这种模式,可修改为其他的模式,渲染进程会开启一个渲染主线程,负责执行HTML、CSS、JS的代码
渲染主线程的主要工作:
1、解析HTML
2、解析CSS
3、计算样式
4、布局
5、处理图层
6、每秒把页面画60次
7、执行全局JS代码
8、执行事件处理函数
9、执行计时器的回调函数
以上的任务都是需要渲染主线程去处理的,那如何调度这些任务呢?谁先谁后?
JS的异步?
代码在执行过程中,会遇到一些无法立即处理的任务,比如:
1、计时完成后需要执行的任务:setTimeout、setInterval
2、网络通信完成后需要执行的任务:XHR、Fetch
3、用户操作后执行的操作:addEventListener
同步:渲染主线程等待任务的时机到达然后执行,依次执行任务。
渲染主线程承担着很多工作,渲染、执行JS代码,如果使用同步的处理方式,就有可能导致主线程产生阻塞,从而导致很多其他任务无法得到执行,主线程阻塞,白白浪费了时间,页面也无法得到更新,给用户造成页面卡死的现象
所以浏览器采用异步的方式来避免这种阻塞,当某些任务发生时,比如计时器、网络、事件监听,主线程会将他们交给其他线程去处理,自身立即结束任务的执行,转而执行后续代码,当其他线程完成时,将事先传递的回调函数包装成任务,加入到任务队列的末尾进行排队,等待被主线程调度执行
在这种异步模式下,浏览器永不阻塞,最大限度的保证了单线程的流畅运行
JS为何会阻碍渲染?
<div>hello</div>
<button>点击切换文案</button>
<script>
let div = document.querySelector('div')
let btn = document.querySelector('button')
btn.onclick = function(){
div.innerText = 'world '
//修改文案后延时5000ms
delay(5000)
}
// 延时函数
function delay(time){
let startTime = Date.now()
while(Date.now()-startTime < time){}
}
</script>
上述代码中,改变div的innerText后会产生一个新的任务(绘制)加入相应的队列中
没有delay(5000)的情况下,btn的回调函数执行结束,退出渲染主线程,将产生的绘制任务加入主线程执行,页面上会立即看到文案的变化
但是由于改变文案后调用了delay函数,死循环5秒,所以在改变文案后,btn的回调函数还没执行完毕,还得继续占用渲染主线程,直到delay跳出循环,btn的回调函数执行结束以后才会将绘制任务加入渲染主线程执行
渲染和JS的执行都在渲染主线程上执行,如果JS的执行时间过长,就会影响到页面的渲染
任务有优先级吗?
任务没有优先级,在队列中先进先出
但是消息队列有优先级
根据W3C的最新解释:
- 每个任务都有一个任务类型,同一个类型的任务必须在一个队列,不同类型的任务可以分属于不同的队列。在一次事件循环中,浏览器根据实际情况从不同队列中取出任务执行
- 浏览器必须准备好一个微队列,微队列中的任务优先于所有其他的任务执行
随着浏览器的复杂度急剧提升,W3C不再使用宏队列的说法
在目前的chrome的实现中,至少包含了下面的队列
- 延时队列:存放计时器到达后的回调任务,优先级:中
- 交互队列:存放用户操作后产生的事件处理任务,优先级:高
- 微队列:存放需要最快执行的任务,优先级:最高
添加任务到微队列的主要方式:promise,MutationObserver
面试题
说一下事件循环?
事件循环又叫消息循环,是浏览器渲染主线程的工作方式
在chrome的源码中,它开启了一个不会结束的for循环,每次循环从消息队列中拿出第一个任务执行,其他线程在合适的时候把任务加入队列末尾
过去把消息队列分为宏队列和微队列,但是随着浏览器的复杂度急剧提升,这种队列模式已经无法满足浏览器的环境,根据W3C的官方解释,每个任务都有自己的任务类型,同类型的任务必须在同一个队列中,不同的任务类型可以属不同的队列,不同的队列有不同的优先级,在一次事件循环中,由浏览器自行决定取哪一个队列的任务进行执行。
js中的计时器能做到精准计时吗?
不能,因为
1、受事件循环的影响,计时器的回调函数只能在主线程空闲时运行
2、根据W3C的标准。当计时器嵌套层级超过五层,则会带有4ms的最少时间
以上截图截自html.spec.whatwg.org/multipage/t…
单线程是异步产生的原因,事件循环是异步的实现方式
因为渲染主线程只有一个,如果采用同步的处理方式,会导致阻塞,为了避免阻塞,采用异步的处理方式。
渲染主线程碰到某些任务时,将这些任务交给其他线程处理,自身立即结束这个任务的执行,转而执行后续代码,当其他线程处理结束后,将事先传递的回调函数包装成任务,加入对应的任务队列的末尾进行排队,等待被主线程调度执行