打开一个页面,都有哪些进程
浏览器主进程、网络进程、渲染进程、GPU进程、插件进程
浏览器如何发送请求的
由网络进程发送http请求,http包含了端口信息,传给tcp层,tcp包装了浏览器的端口号和服务器的(http为80,https为443),传给ip层,ip层包含了电脑所连接的ip地址与目标ip,如果是视频流的话这里的tcp可能变成为upd。
在http和tcp之间,为了安全又做了层ssl加密
https
https 主要做了两件事校验证书 和 加密报文,证书是校验ca机构信息、网站的合法性,证书中含有公钥,由非对称加密方式与服务器生成一个密钥,对报文进行对称加密。
浏览器的事件循环机制
简单的讲浏览器运行js是单线程执行的,这样的话异步任务需要排队,浏览器分为V8主线程、网络线程、事件驱动线程、定时器线程、渲染线程分工执行。
刚开始的script代码被当做第一个宏任务、执行过程中遇到其他宏任务(setTimeout、ajax等)把这些任务交给对应线程去处理,在维护一个消息队列,当异步任务执行完后直接进入队列。
当主线程的同步代码执行完之后,在维护一个微任务队列去执行当前任务中的微任务,eventLoop机制启动,取出队列中最早的任务,放到主线程去执行,完成一次循环。
这里注意一个问题,就是渲染页面的时机在微任务执行完和下一个宏任务执行开始前去渲染页面,渲染页面和执行代码是互斥的。
主调用栈是怎么运行的
刚开始执行js时,开辟调用栈,创建全局执行上下文,全局执行上下文入栈,每遇到一个函数,就创建一个函数执行上下文,入栈,函数内套函数,则内部函数的上下文入栈,一个函数执行完毕后,其上下文会出栈函数内的变量会销毁(闭包除外);
上下文中保存着:变量环境,词法环境,outer、this
在每个执行上下文的变量环境中,都包含了一个外部引用,用来指向外部的执行上下文,我们把这个外部引用称为 outer。
故查找变量的顺序是 首先在当前执行上下文的变量环境中查找,之后在outer对应的上下文变量环境中查找,这条链被成为作用域链。
垃圾回收机制
V8把堆内存分为新生代和老生代,副垃圾回收器,主要负责新生代的垃圾回收。主垃圾回收器,主要负责老生代的垃圾回收。
新生代
用 Scavenge 算法来处理。所谓 Scavenge 算法,是把新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域,如下图所示
新加入的对象都会存放到对象区域,当对象区域快被写满时,就需要执行一次垃圾清理操作。
在垃圾回收过程中,首先要对对象区域中的垃圾做标记;标记完成之后,就进入垃圾清理阶段,副垃圾回收器会把这些存活的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来,所以这个复制过程,也就相当于完成了内存整理操作,复制后空闲区域就没有内存碎片了。完成复制后,对象区域与空闲区域进行角色翻转,也就是原来的对象区域变成空闲区域,原来的空闲区域变成了对象区域。
这样就完成了垃圾对象的回收操作,同时这种角色翻转的操作还能让新生代中的这两块区域无限重复使用下去。
老生区
对象比较大,若要在老生区中使用 Scavenge 算法进行垃圾回收,复制这些大的对象将会花费比较多的时间,从而导致回收执行效率不高,同时还会浪费一半的空间。
因而,主垃圾回收器是采用标记 - 清除(Mark-Sweep)的算法进行垃圾回收的。下面我们来看看该算法是如何工作的。首先是标记过程阶段。标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,
能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。
比如堆中的数据有 1.5GB,V8 实现一次完整的垃圾回收需要 1 秒以上的时间,这也是由于垃圾回收而引起 JavaScript 线程暂停执行的时间,若是这样的时间花销,那么应用的性能和响应能力都会直线下降,为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,
我们把这个算法称为增量标记(Incremental Marking)算法。
V8 是如何执行一段 JavaScript 代码的
V8 依据 JavaScript 代码生成 AST 和执行上下文,再基于 AST 生成字节码,然后通过解释器执行字节码,通过编译器来优化编译字节码。