1. 浏览器组成
用户界面(User Interface)
用户界面主要包括工具栏、地址栏、前进/后退按钮、书签菜单、可视化页面加载进度、智能下载处理、首选项、打印等。除了浏览器主窗口显示请求的页面之外,其他显示的部分都属于用户界面。
用户界面还可以与桌面环境集成,以提供浏览器会话管理或与其他桌面应用程序的通信。浏览器引擎(Browser Engine)
在用户界面和渲染引擎之间传送指令。渲染引擎(Rendering Engine)
显示请求的内容。网络(Networking)
网络调用,如http请求。JavaScript解析器(JavaScript Interpreter)
JavaScript解释器能够解释并执行嵌入在网页中的JavaScript(又称ECMAScript)代码。 为了安全起见,浏览器引擎或渲染引擎可能会禁用某些JavaScript功能,如弹出窗口的打开。显示后端(Display Backend)
显示后端提供绘图和窗口原语,包括:用户界面控件集合、字体集合数据持久层(Data Persistence)
数据持久层将与浏览会话相关联的各种数据存储在硬盘上。 这些数据可能是诸如:书签、工具栏设置等这样的高级数据,也可能是诸如:Cookie,安全证书、缓存等这样的低级数据。
2. 浏览器内核
浏览器 | 渲染引擎(内核) | JS引擎 |
---|---|---|
Chorme | Webkit->Blink | v8 |
FireFox | Gecko | SpiderMonkey |
Safari | Webkit | JavaScriptCore |
Opera | Presto->Webkit->Blink | JavaScriptCore |
Edge | EdgeHTML | Chakra(for JavaScript) |
IE | Trident | JScript(IE3.0-IE8.0) |
3. 架构
现代浏览器多为多进程架构
Browser进程
- 浏览器的主进程(负责协调、主控),该进程只有一个
- 负责浏览器界面显示,与用户交互。如前进,后退等
- 负责各个页面的管理,创建和销毁其他进程
- 将渲染(Renderer)进程得到的内存中的Bitmap(位图),绘制到用户界面上
- 网络资源的管理,下载等
插件进程
- 每种类型的插件对应一个进程,当使用该插件时才创建
GPU进程
- 该进程也只有一个,用于图形绘制等等
渲染进程
- 即通常所说的浏览器内核(Renderer进程,内部是多线程)
- 每个Tab页面都有一个渲染进程,互不影响
- 主要作用为页面渲染,脚本执行,事件处理等
网络进程
- 负责发起和接受网络请求
4. 渲染进程
浏览器渲染进程是多线程模型,主要包含如下线程:
GUI渲染线程
- 解析HTML,CSS,构建DOM树、构建render树,布局和绘制等
- 界面重绘(Repaint)或回流(reflow)
JS引擎线程
- 处理JavaScript程序
- JS引擎一直等待着任务队列中任务的到来,然后加以处理
- 和GUI渲染线程是互斥的
事件触发线程
- 属于浏览器而不是JS引擎,用来控制事件循环,并且管理着一个事件队列(task queue)
- 当JS引擎执行如setTimeOut代码块时,或者是其他的如网络异步请求、鼠标点击等时,会将对应任务添加到事件线程中。当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理的事件队列中,等待JS引擎空闲时处理。
定时器线程
- setInterval与setTimeout所在线程。计时完毕后,将对应事件添加到事件队列中,等待JS引擎空闲时处理。
异步网络请求线程
- 处理网络请求,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。等待JS引擎空闲时处理。
5. 页面渲染流程
- HTML 解析器解析 HTML 文档,生成 DOM 树 (DOM Tree)
- CSS 解析器解析 CSS 文档,生成 CSSOM (CSS Object Model)
- 将DOM树和CSSOM结合,生成渲染树(Render Tree)
- 根据渲染树来布局(Layout),以计算每个节点的几何信息
- 将各个节点绘制(painting)到屏幕上
6. 回流和重绘
回流(reflow)
Render Tree
中部分或全部元素的几何尺寸发生改变,浏览器重新计算元素的几何属性并调整布局的过程,也称重排。
💡触发场景:
- 页面首次渲染
- 浏览器的窗口尺寸变化
- 元素的位置/尺寸/内容/字体大小发生变化
- 添加或删除可见的DOM元素
- 查询一些属性或方法
- clientTop、clientLeft、clientWidth、clientHeight
- offsetTop、offsetLeft、 offsetWidth、offsetHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight
- scrollIntoView()、scrollIntoViewIfNeeded()、getComputedStyle()、getBoundingClientRect()、scrollTo()
重绘(repaint)
Render tree
中的一些元素只发生样式的改变并不影响它在文档流中的位置时,浏览器不需重新计算元素的几何属性、直接为元素绘制新的样式的过程。
💡触发场景:
- 回流
- 一些CSS属性的改变
- color、border-style、visibility、background、text-decoration、background-image、background-position、background-repeat、outline-color、outline、outline-style、border-radius、outline-width、box-shadow、background-size
关系
回流必将引起重绘,重绘不一定会引起回流。
7. 浏览器缓存机制
浏览器的缓存机制,通常是指我们常说的HTTP缓存机制,主要分为协商缓存
与强缓存
,缓存机制的运行流程如下:
- 浏览器在进行HTTP请求前,会先向缓存中查找该请求结果,根据该缓存结果的缓存标识
Expires
和Cache-Control
判断是否命中强缓存,是则直接使用该缓存结果,不会发请求到服务器。 - 如果没有命中强缓存或强缓存已失效,浏览器会发送一个请求到服务器,通过携带缓存标识
Last-Modified / If-Modified-Since
和Etag / If-None-Match
验证资源是否命中协商缓存,如果命中,服务器会返回304,依然是从缓存中读取资源 - 如果没有命中或协商缓存失效,服务器会重新返回完整的资源
附:
Expires
是HTTP/1.0
中的控制字段,是绝对值,受限于本地时间;Cache-Control
是HTTP/1.1
中的字段,记录的时间是相对值,Cache-Control
的优先级高于Expires
- 浏览器读取缓存会优先从内存缓存中读取(from memory cache),其次从硬盘读取(from disk cache)
- 控制协商缓存的字段
Etag / If-None-Match
的优先级比Last-Modified / If-Modified-Since
高
8. V8垃圾回收机制
V8采用的是分代式回收, 即将堆分为新生代和老生代两类,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的对象。V8分别使用两个不同的垃圾回收器来实施垃圾回收。
- 新生代垃圾回收器 -
Scavenge
- 老生代垃圾回收器 -
Mark-Sweep
&Mark-Compact
针对新生代堆,Scavange算法将其分为两部分,分别叫from
空间和to
空间,工作过程就是将from
空间中存活的活动对象复制到to
空间中,并将这些对象的内存有序的排列起来,然后将from
空间中的非活动对象的内存进行释放,完成之后,将from
空间和 to
空间进行互换。
V8引擎采用了对象晋升
策略,在下一次垃圾回收时,已经进行过一次垃圾回收依然还存活的对象或to
空间的内存占比超过25%,对象会被移动到老生代区中。
之所以有25%的内存限制是因为to
空间在经历过一次Scavenge算法后会和from
空间完成角色互换,会变为from
空间,后续的内存分配都是在from
空间中进行的,如果内存使用过高甚至溢出,则会影响后续对象的分配。
针对老生代堆,采用的是 标记-清除(Mark-Sweep)
和 标记-整理(Mark-Compact)
算法。标记-清除
就是从一组根元素开始,递归遍历这组根元素(遍历调用栈),在这个遍历过程中,能到达的元素标记为活动对象,然后在第二次遍历过程中清除未被标记的对象(非活动对象)。清除算法后,会产生大量不连续的内存碎片,而碎片过多会导致大对象无法分配到足够的连续内存。于是又产生了标记-整理
算法,这个标记过程仍然与标记-清除
算法里的是一样的,但后续不是对非活动对象进行清理,而是让活动对象都向一端移动,然后直接清理掉端边界以外的内存,从而让存活对象占用连续的内存块。