浏览器原理学习笔记(一)
浏览器原理学习笔记(一)
浏览器是怎么渲染页面的
-
根据html构建dom树:从上往下,线性解析,遇到script标签暂停解析,先执行js代码(对于图片、css link、设置了defer、async的script标签,不会暂停解析)拓展点:生成dom树前,浏览器会预扫描html中的代码,将css文件、字体、js文件下载下来,这个过程是异步的。
-
根据css文件生成css-object-model树。
-
合并cssom树和dom树,生成渲染树。渲染树中不会有display:none的元素。
-
布局:计算所有(display:不为none)的结点的位置、大小。这时如果遇到未指定大小的图片,浏览器会忽略当前图片的大小,等到图片获取完成再去根据图片的宽高、位置,再次计算受影响的结点布局,这个过程称为“回流”。
-
分层:光是获得各个元素的位置信息是不够的,浏览器还需要为一些设置了Z-index的结点生成专用的图层,最终生成一个图层树。生成专用的图层的方式:
- 有定位属性的元素、有z-index的元素、有透明属性的元素等
-
图层绘制:这一步并没有真正开始绘制,而是生成绘制列表。把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。
-
栅格化操作:当页面很长的时候,有的图层很大,但是用户可见视口只能看到一部分,如果直接把整个页面图层加载出来,非常耗费性能。这时候浏览器渲染进程就会把图层分成一个个的图块,在视口附近的图块会优先生成位图。所谓的栅格化操作就是把图块转换成位图的过程,这个过程一般是由GPU参与的,渲染进程把生成图块的指令发送给GPU,然后在GPU中执行生成图块的位图,并保存在GPU的内存中。
-
绘制:一旦所有图块都被栅格化,合成线程就会生成一个绘制图块的命令,然后将该命令提交给浏览器进程。浏览器进程里面有一个叫viz的组件,用来接收合成线程发过来的绘制图块命令,然后根据命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
讲一下V8垃圾回收吧
-
V8会把堆分成新生代和老生代两个区域,新生代区域存放存在时间短的对象,老生代区域存放存活时间久的对象。新生代区域一般只支持1-8M的存储空间,而老生代区域则很大。
-
V8有两个垃圾回收器:主垃圾回收器和副垃圾回收器。副垃圾回收器负责新生代区域的垃圾回收,主垃圾回收器负责老生代区域的垃圾回收。
-
副垃圾回收器:
-
新生代中虽然存储空间小,但是垃圾回收是很频繁的,因为一般来说新生成的对象都会存储在新生代空间中。
-
新生代中使用 Scavenge 算法进行垃圾回收。Scavenge 算法:
-
将新生代分为两个区域,对象区域和空闲区域,新产生的对象都会加入对象区域,当对象区域存储被写满时,就会对对象区域进行垃圾清理,标记对象区域的垃圾,然后进行清除。
-
对象区域垃圾清理后仍然存活的对象就会被移动到空闲区域,这个复制并且移动的过程同时解决了内存碎片的问题。复制移动的过程比较耗费性能,所以新生代区域会设置的比较小,兼顾运行效率。
-
在完成移动后,对象区域和空闲区域会进行角色的互换,原来的对象区域变成空闲区域,原来的空闲区域变成对象区域。
-
当新生代的区域中的一个对象经历了两次垃圾回收仍然存活,就会进行对象晋升策略,将该对象移动到老生代区域
-
-
-
主垃圾回收器:
-
老生代存储的对象,除了有新生代中晋升的对象,还有一些比较大的对象会直接写入到老生代的空间中。
-
老生代不适合用 Scavenge 算法,因为这些对象太大了,复制移动过程十分耗费性能,效率低下。
-
老生代最初是直接使用标记清除法进行垃圾回收。但是单纯的标记清除后会有很多内存碎片,导致内存空间利用性太低。
-
为了解决内存碎片问题,产生了标记-整理算法。这个算法大体上和标记清除算法一样,但是并不是把未存活对象的内存空间释放,而是把所有存活对象整理(移动)到内存空间的一端,然后释放存活对象边界以外的所有内存空间。
-
-
全停顿:
-
什么是全停顿:垃圾回收是运行在主线程之中的,在垃圾回收的过程中,JS代码的执行会停止,等待垃圾回收结束后再执行。
-
弊端:对于新生代区域来说,不会有什么问题,因为新生代区域太小了,就算停顿也不会影响应用和用户的交互。但是对于老生代区域来说,执行一次完整的垃圾清理的耗时严重阻碍了用户体验。
-
增量标记算法:为了降低老生代的垃圾回收造成的卡顿问题,V8将垃圾回收算法的标记过程分成了一个个耗时小的子标记过程,让子标记过程可以和JS代码的执行交替进行,直到完成所有子标记过程完成再进行垃圾清理。
-
使用增量标记算法,可以把一个完整的垃圾回收任务拆分为很多小的任务,这些小的任务执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行,这样当执行上述动画效果时,就不会让用户因为垃圾回收任务而感受到页面的卡顿了。
-
了解浏览器进程架构吗?
1个浏览器(Browser)主进程、1个 GPU 进程、1个网络(NetWork)进程、多个渲染进程和多个插件进程
-
浏览器进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
-
渲染进程。核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎Blink和JavaScript引擎V8都是运行在该进程中,默认情况下,Chrome会为每个Tab标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
-
GPU进程。其实,Chrome刚开始发布的时候是没有GPU进程的。而GPU的使用初衷是为了实现3D CSS的效果,只是随后网页、Chrome的UI界面都选择采用GPU来绘制,这使得GPU成为浏览器普遍的需求。最后,Chrome在其多进程架构上也引入了GPU进程。
-
网络进程。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
-
插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响
说一下回流与重绘吧
-
回流是什么:当渲染树中部分或者全部元素的尺寸、结构等发生改变时,浏览器会重新布局,并重绘页面。
-
如何触发回流:浏览器窗口大小改变、元素尺寸或者位置改变、元素内容改变(图片加载完成,或字体大小改变)、添加或删除DOM元素等
-
重绘是什么:浏览器根据元素样式的改变重新绘制页面。
-
如何触发重绘:
- 回流一定会触发重绘,
- 页面中元素的样式改变不影响结点在文档流中的位置时(color改变、background-color、visibility等),不会触发回流,会直接触发重绘。