✊构建浏览器工作原理知识体系(浏览器内核篇)

🌻 前言

书接上回~

系列文章目录:

  1. # ✊构建浏览器工作原理知识体系(开篇)
  2. # ✊构建浏览器工作原理知识体系(浏览器内核篇)
  3. # ✊构建浏览器工作原理知识体系(网络协议篇)
  4. ✊构建浏览器工作原理知识体系(网页加载超详细全过程篇) 待更新

为什么你觉得偶尔看浏览器的工作原理,但总是忘呢😵‍💫,因为你没有形成一个完整的知识网络,你的记忆是碎片化的。正如人的神经网络,只有当你的记忆相互依赖,相互链接,才能形成长期稳定的记忆。

所以本系列文章我将用一条知识线将浏览器工作原理的知识串联起来,因为本文的目的是为了帮助大家建立浏览器基础的思维树,所以很多细节点不做过多阐述,先有了树,后面你在上面伸展枝叶就会发现清晰明了很多。欢迎点赞支持或评论指正。

🪴一、渲染进程的工作

先来大概了解下渲染进程的职责:

  1. HTML 解析: 渲染进程负责解析 HTML 文档,构建 DOM 树;
  2. CSS 解析: 解析样式表,生成 CSS 规则树(Style Rules / CSSOM );
  3. 合成渲染树: 将 DOM 树和 CSS 规则树合成为渲染树(Render Tree),该树只包含需要渲染的节点(会去除display:none的元素);
  4. 布局(Layout): 计算每个渲染树节点的几何信息,包括位置和大小,生成布局树;
  5. 绘制(Paint): 将渲染树的每个节点绘制到屏幕上,创建位图(Bitmap);
  6. 合成(Composite): 将各个图层按正确的顺序(根据层叠顺序z-index和透明度等因素)合成为最终的页面画面,提高绘制性能;
  7. 处理用户输入: 监听用户输入事件(鼠标点击、键盘输入等),并触发相应的事件处理程序;
  8. 脚本执行: 执行页面中的 JavaScript 脚本,响应用户交互、更新页面内容;
  9. 处理网络请求: 处理页面发起的网络请求,包括获取 HTML、CSS、JavaScript 文件等;
  10. 插件管理: 如果页面使用了插件(如 Flash、PDF 阅读器等),渲染进程会与插件进行交互;
  11. 内存管理: 管理渲染进程的内存使用,确保不发生内存泄漏等问题;
  12. 安全性: 实施浏览器的安全策略,防止恶意脚本和攻击;

🪴二、浏览器内核和JS引擎

以前人们常把浏览器内核分为渲染引擎和 Javascript 引擎。后面有了更明确的区分,浏览器内核单指渲染引擎,Javascript 引擎独立了出来。

所以浏览器内核,也就是渲染引擎,也可以叫排版引擎。浏览器内核是浏览器最核心的部分,负责对网页语法的解释并渲染(显示)网页。

Javascript 引擎的主要工作是将Javascript代码转换为快速优化的机器码,以便浏览器或服务器能够解释和执行。另外它还负责执行代码、分配内存以及垃圾回收。

(一)常见的浏览器内核和JS引擎

浏览器内核厂商兼容前缀备注JS引擎
Chromewebkit > BlinkGoogle-webkit-2008年以前chrome用的是webkit内核,之后改用的Blink其实是webkit的分支V8
SafariWebKitApple-webkit-其实Safari才是WebKit内核的鼻祖,只是Chrome 广为人知且对WebKit有所贡献,所以一说到webkit,第一时间想到的是chromeNitro Javascript
IE、EdgeTrident > EdgeHTML微软-ms-国内很多浏览器都使用了Trident和Blink双内核,例如360、ucJScript/Chakra
FireFoxGeckomozilla基金会-moz-SpiderMonkey > TraceMonkey > JaegerMonkey
Operapresto > webkit > Blink挪威Opera Software-o-Linear A/ Linear B/ Futhark/ Carakan

另外,在移动端,还有UC浏览器的u3内核,它是首个中国创造的浏览器内核,由UC研发团队耗时三年时间打造而成。以及腾讯系App内置webview(例如qq浏览器)的x5内核。这俩其实也是基于webkit内核改造的。

🪴三、渲染进程和浏览器引擎的关系

上一篇文章说到:浏览器渲染进程有5大类线程:GUI渲染线程、JS引擎线程、事件触发线程、定时器线程、异步HTTP请求线程。

从名称其实也能看出来,GUI渲染线程是基于渲染引擎工作的,JS引擎线程是基于 JS 引擎工作的,而其他三个线程是浏览器内部机制在处理。

所以说浏览器渲染进程与浏览器引擎之间的关系是协同工作的关系,共同实现了浏览器的核心功能。

🪴四、关于资源加载阻塞

首先我们要知道,js、css、图片等静态资源文件都是在网络进程中进行下载的。并且网络进程具有 并行下载 的能力,能够同时下载多个文件。而浏览器在解析页面之前,会启动一个 预解析 的线程,和网络进程通信,提前开始并行下载引入的外部 css、js文件。

另外,浏览器针对同一个域名内资源请求的并行连接数量有限制(为了防止DDOS攻击),所以一般网站为了加速资源下载,会做域名分散,这里就不多介绍了,想了解的可以看我以前写的一篇文章:域名发散

(一)JS文件加载阻塞

上一篇文章中我提到了,因为GUI渲染线程和JS引擎线程都需要访问和操作Dom,为了线程安全所以它们设计为互斥机制,即JS引擎线程工作时,GUI线程就会挂起,所以可以得出结论:js的加载会阻塞页面渲染。

那怎么就能不让 js 文件加载阻塞渲染呢?很简单,只需要把js文件放在最后加载或者在script标签上加上async或者defer属性的话,js加载就能变成异步的,不阻塞渲染,使用如下:

<script  async src="https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.cjs.js"></script>
<script  defer src="https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.cjs.js"></script>

那它们俩有什么区别呢?

首先我们要搞清楚一个概念,js的下载是无法阻止的。并且正如上面所说,js是可以并行下载的,所以js只是同步执行,并非下载也是同步的,很多网上的文章描述会让人引起误会,总是说async和defer可以把js下载和运行变成异步的。设置async、defer属性只是影响了js文件的执行时机,对下载并无影响。

  • async:js文件异步执行,哪个先下载完哪个先执行
  • defer:延迟执行,在文档被解析后,并且触发DOMContentLoaded事件之前执行。

(二)CSS文件阻塞渲染

css文件的加载不会阻塞Dom树的构建,因为Dom树和css规则是并行解析的,互不影响。但是css加载会阻塞渲染树的合成,所以css加载也会阻塞渲染。

🪴五、渲染引擎工作流程

  1. HTML解析: 浏览器内核的 HTML Parse 将 HTML 转化为DOM树(DOM Tree);
  2. css解析: 同时CSS Parse 将css转化为 Cssom Tree(Style Rules),可以用 document.styleSheets 查看除了内联和默认样式之外的所有内部和外部样式表**;
  3. 生成渲染树: 然后 DOM树(DOM Tree)和CSS规则(Style Rules)通过附加(Attachment)生成渲染树(Render Tree),设置了display: none的元素会在渲染树中去除。
    这期间会进行样式计算,很多预设值会变成绝对值,比如red会变成rgb(255,0,0);相对单位会变成绝对单位,比如rem会变成px。日常开发想查看我们预设值计算出来的实际值,可以点击属性右键查看计算得出的值,当然你也可以切换计算样式面板查看全部属性计算后的值。

在这里插入图片描述

  1. 布局: 然后计算DOM树中可见元素的几何位置(例如节点的宽高、相对包含块的位置),生成布局树;

  2. 分层: 渲染主线程会对整个布局树中进行分层。一般滚动条、a标签、transformwill-change等样式都会影响分层效果,另外,像opacityfilter等属性虽说也能影响分层,但直接设置一般不会有效,需要设置动画animation才会独立分层。所以如果你想查看哪些元素单独分层了,可以在控制台的图层面板查看(设置这些属性的元素分层渲染可以触发GPU硬件“加速”):

在这里插入图片描述

  1. 绘制: 渲染引擎将将页面内容绘制到帧缓冲区(Framebuffer)中,帧缓冲区是一个内存区域,用于存储图像数据,这些图像数据最终会被 GPU 渲染到屏幕上。至此,渲染主线程的工作已经完成了,接下来由其他线程处理显示图像;

  2. 分块: 合成器线程首先对每个图层进行分块,将其划分为更多的小区域;

  3. 光栅化: 上面我们已经获得了文档结构、元素的样式、元素的几何关系、绘画顺序,接下来把这些信息转化为显示器中的像素才能显示,这个转化的过程,就叫做光栅化。此过程是合成器的光栅工作线程把每个块变成位图,位图可以理解成内存里的一个二维数组,这个二维数组记录了每个像素点信息;

  4. 合成: 合成器线程再将以上的像素信息生成合成帧,合成帧就是页面一个帧的内容的绘制四边形集合(绘制四边形是包含图块在内存的位置以及图层合成后图块在页面的位置之类的信息);

  5. 显示: 最后合成器线程会把出现在视口区的合成帧提交给浏览器进程,即渲染帧,最后浏览器进程将渲染帧发送给GPU从而展示在屏幕上,如果有页面滚动,则合成器线程会构建另外一个合成帧发送给GPU来更新页面;

额外补充:

  • 其实以上最后几个阶段,现在主流的浏览器已经都支持在GPU上处理分块、光栅化、合成等操作了,具体可以看下一章;
  • 除了修改dom样式,获取DOM的某一些值的其实也会立即引起reflow回流,会引起回流的属性:https://gist.github.com/paulirish/5d52fb081b3570c81e3a

🪴六、关于css硬件加速

(一)CPU 和 GPU

先从两个角度简单区分下什么是 CPU 和 GPU:

  1. 用途:
  • CPU:CPU 是一种通用处理器,主要用于执行各种计算任务,例如处理操作系统、运行应用程序、执行算法等;
  • GPU:GPU 是图形处理器,主要用于处理图形和图像相关的计算任务,例如图形渲染、图像处理、视频解码等;
  1. 性能特点:
  • CPU:CPU 的性能特点是在单个任务上表现出较高的性能和灵活性,适用于处理复杂的、多变的计算任务
  • GPU:GPU 的性能特点是在大规模并行计算上表现出较高的性能和吞吐量,适用于处理大规模的、高度并行、重复的计算任务

(二)网页渲染和 CPU/GPU 的关系

以往网页渲染时,网页渲染过程中,渲染引擎对页面元素的样式布局计算、绘制等操作,通常都是在 CPU 上进行的。只有在最后显示时,GPU 才会工作处理像素信息显示网页。

不过目前主流浏览器都已经支持 CPU 或者 GPU 进行光栅化、合成操作了。例如 Chrome浏览器。默认是开启 GPU 加速,即由 GPU 处理分块、光栅化、合成操作(当然 GPU 加速带来的好处不止这些,还有例如3D图形加速和视频硬件解码加速,以及 GPU 和 CPU 并行带来的性能优化)。

在这里插入图片描述

开启 GPU 加速之后,我们可以打开控制台的渲染面板查看 GPU 的内存情况和帧渲染的信息,如下:

在这里插入图片描述

在这里插入图片描述

如果你想查看 GPU 的相关信息和状态,也可以使用 Chrome 浏览器的 chrome://gpu 页面。在里面可以查看 GPU 驱动程序的版本、GPU 加速是否已启用等信息,如果没有启用,会显示警告和错误信息,可以根据这些信息排查一些问题。

PS: 渲染面板功能非常多,例如在这里可以开启“图层边框”,也可以查看网页的分层情况。还可以利用“布局偏移区域”,开启后排查网页会引起回流的元素,以优化网页渲染问题。

(三)CSS硬件加速的原理

css硬件加速,简单来说,就是使用某些 css 属性后,触发 gpu 工作加速渲染指定元素。

这里的 GPU 硬件加速和上一节中设置开启 GPU 加速是两个概念。上面开启 GPU 硬件加速是把页面渲染的一些图形操作环节移至 GPU 处理,减轻 CPU 压力,优化渲染性能,而这一节要说的 GPU 加速是把指定元素绘制到独立的图层,由 GPU 完成所有计算,这样不仅加速了图形渲染,还不会造成页面的回流重绘,从而优化了动画性能。

例如我设置了一个背景色变化的动画,可以看到性能面板的底部摘要饼图,渲染、绘制时间一直在增加。而一个透明度opacity变化的动画,渲染、绘制时间是不会增加的。因为opacity动画触发了css硬件加速。

在这里插入图片描述

在这里插入图片描述

总结: GPU 硬件加速的原理有两个原因:

  • 使用某些css属性后,渲染引擎会把该把元素单独分层交给 GPU 渲染,GPU处理图形计算更快;
  • 分层渲染不会造成页面的回流重绘;

🪴七、JS引擎工作原理

(一)工作流程概述

这里主要说下V8引擎解析的工作流程:

  1. 词法分析(Lexical Analysis): 解析器首先对JavaScript代码进行词法分析,将代码分解为一系列的标记(token)。这些标记是语法上有意义的最小单元,包括关键字、标识符、运算符、分隔符等。
  2. 语法分析(Syntax Parsing): 接下来,解析器将标记转化为抽象语法树(Abstract Syntax Tree,简称AST)。AST是代码语法结构的一种抽象表示,便于后续处理。
  3. 生成字节码(Bytecode Generation): AST被传递给V8引擎的解释器(Ignition),它将AST转换为字节码。字节码是一种中间表示,不同于机器代码,但比源代码更容易解释和执行。
  4. 解释执行: 字节码由解释器执行。在这个阶段,代码的基本逻辑会被逐行解释执行。这使得代码能够在不等待整个编译过程完成的情况下开始执行。
  5. 即时编译(Just-In-Time Compilation,JIT): 如果引擎检测到某个函数或代码块被频繁执行,它可能会触发即时编译器(TurboFan)来将字节码转换为本机机器代码。本机机器代码的执行速度更快,因此通过JIT编译可以提高性能。
  6. 优化和内联: 引擎会进行一系列的优化,包括内联缓存、类型反馈等。这些优化旨在使执行更加高效。
  7. 垃圾回收: 引擎使用垃圾回收器来管理内存,识别不再使用的对象并释放它们。
  8. 执行结果: 最终,JavaScript代码的执行结果被返回给用户。

(二)流程图

下面是我画的一张流程图:

在这里插入图片描述

V8 引擎采用了一种称为 “混合执行(Hybrid Execution)” 的策略,将解释执行和即时编译执行结合起来,以在不同情况下实现最佳的性能和响应时间。简单来说就是判断某段代码是否频繁执行,并且有利于性能提升,如果是,则交给TurboFan即时编译器处理,否则还是由解释器执行。

(三)优化手段

除了对热点函数使用即时编译外,js引擎还有一些其他的优化手段,例如:

  • 内联缓存(Inline Caching,简称IC) :V8 引擎会根据对象的类型和属性访问的上下文动态地生成内联缓存,以避免不必要的查找操作,从而提高访问速度。简单来说,就是在 V8 执行代码时,会把函数中的一些关键数据缓存起来,下次执行该函数时就可以节省获取这个数据的时间了,以提升一些重复代码的执行效率;

  • 预解析(Pre-Parsing) :V8 引擎可以在执行 JavaScript 代码之前对其进行预解析,以提前分析代码结构和语法,并生成相应的解析树和抽象语法树(AST)。这可以加速代码的执行过程,尤其是在代码需要频繁执行的情况下。

🪴八、垃圾回收机制

浏览器的垃圾回收机制是一种自动管理内存的机制,用于检测和释放不再使用的内存,以减少内存泄漏和提高系统性能。

(一)内存生命周期

内存分配和释放的过程(生命周期)分为以下几个阶段:

  1. 分配内存:当我们申请变量、函数、对象的时候,系统会自动为它们分配内存;
  2. 内存使用:即读写内存,也就是使用变量、函数等;
  3. 内存回收:使用完毕,由垃圾回收机制自动回收不再使用的内存;

(二)栈内存、堆内存

堆内存(Heap)和栈内存(Stack)是计算机内存的两个主要区域,它们分别用于存储不同类型的数据,

  • 堆(Heap): 堆是用于动态分配内存的区域,用于存储引用类型的数据,如对象和数组。在堆中分配的内存不会自动释放,需要通过垃圾回收机制来回收不再使用的内存。堆的大小通常比栈大,并且可以动态增长和收缩。
  • 栈(Stack): 栈是用于管理函数执行上下文和存储基本类型值的一种数据结构,其分配和释放由执行上下文(Execution Context)的进入和退出决定。

动态数据存储在堆内存中,同时会把其内存地址存到栈内存中。所以如果一个对象的引用存储在栈内存中,即使执行上下文被弹出,这个对象仍然存在于堆内存中,只要还有其他引用指向它,它就不会被垃圾回收机制清除。

(三)两种基础的垃圾回收机制

1. 引用计数法

引用计数法:每个对象维护了一个引用计数器,记录着当前有多少个指针指向该对象。当引用计数器减为零时,说明该对象不再被引用,可以被释放。

优势:

  1. 可即刻回收垃圾,当被引用数值为0时,对象在变成垃圾的时候就立刻被回收。
  2. 因为是即时回收,所以程序不会暂停去单独使用很长一段时间的GC,那么最大暂停时间很短。

缺点:

  1. 时间开销大,因为引用计数算法需要维护引用数,一旦发现引用数发生改变需要立即对引用数进行修改;
  2. 最大的缺点还是无法解决循环引用的问题;

看一个例子,就能很鲜明的看出引用计数存在的缺点了:

function foo() {
  const A = {};
  const B = {};

  A.foo = B;
  B.foo = A;

  return "hello abin";
}

foo();

很明显,上面函数 foo() 内创建了两个对象 AB,并相互引用了对方,形成了一个循环引用。这样即使foo函数执行完,A、B的引用数也不会变为0,就会造成内存泄漏。

解决办法:手动把变量设置为null

2. 标记清除法(标记整理法、可达性分析法)

标记清除法:从根对象(通常是全局对象,可以理解为windows)开始,遍历内存中所有对象的引用关系,如果是能访问到的对象,则标记为可达对象(无法访问的为不可达对象),标记所有可达对象,最后清除未被标记的对象,实现内存的自动回收。

优点:

  1. 实现简单,打标记也就是打或者不打两种可能,所以就一位二进制位就可以表示;
  2. 解决了循环引用的问题;

缺点:

  1. 内存碎片化(内存零零散散的存放,造成资源浪费);
  2. 再分配时遍历次数多,如果一直没有找到合适的内存块大小,那么会遍历空闲链表(保存堆中所有空闲地址空间的地址形成的链表)一直遍历到尾端;
  3. 不会立即回收资源;

优化:

为了优化标记清除法内存碎片化的问题,通常会在标记后引入整理阶段,将存活的对象整理到一起,以释放出连续的内存空间,提高内存的利用率。

整个过程为:

标记

整理

在这里插入图片描述

清除
在这里插入图片描述

(四)V8引擎的垃圾回收机制(分代垃圾回收)

V8引擎的垃圾回收机制采用了分代回收策略,将堆内存中的对象按照存活时间分为不同的代(Generation),通常分为新生代(Young Generation)和老生代(Old Generation)两个代。然后这两代垃圾采用不同的垃圾回收机制处理。

在这里插入图片描述

新生代

新生代存放的是存活时间较短的对象(经过一次垃圾回收后,就被释放回收掉),由副垃圾回收器管理,通常使用复制算法(Copying Algorithm)来进行垃圾回收。

回收流程:

  1. 触发回收:新对象首先被分配到From空间中,当From空间被占满时,就会触发垃圾回收机制;
  2. 标记阶段:从根对象遍历对象引用关系,标记所有活动对象;
  3. 复制阶段:将所有活动对象从 From 空间复制到 To 空间,并且进行排序,使得To空间成为连续的内存块;
  4. 清除阶段:对 From 空间进行清理,回收非活动对象所占用的内存空间。
  5. 空间交换:清除完成后,From空间和To空间相互交换,即From空间变为To空间,To空间变为From空间。这样,下一次的垃圾回收就可以在新的To空间中进行。

新生代晋升老生代机制:

  1. 年龄达到阈值:每个对象都有一个年龄计数器,初始为0。每次经过一次垃圾回收,如果对象仍然存活,它的年龄计数器就会加1。当年龄计数器达到阈值时,对象就会被晋升到老生代内存;
  2. To空间的内存占用达到一定比例:当To空间的内存占用超过一定比例(通常是25%到50%)时,也会触发对象的晋升。这是为了避免新生代内存过快地被填满,导致频繁的垃圾回收。

老生代

新生代存放的是存活时间较长的对象(经过多次垃圾回收后仍存在),由主垃圾回收器管理,通常使用标记-整理-清除法来进行垃圾回收。

Orinoco优化

Orinoco是目前v8引擎的垃圾回收器,因为垃圾回收存在全停顿问题(在进行垃圾回收操作时,整个应用程序的执行都会被暂停),可能会导致页面卡顿,所以Orinoco采用了一些优化手段。

当然了,新生代占用内存较小,活动对象也比较少,所以全停顿的影响不大,以下优化手段主要是针对老生代的:

  • 并行垃圾回收 (parallel)
  • 增量垃圾回收 (incremental)
  • 并发垃圾回收 (concurrent)
  • 惰性清理

1. 并行垃圾回收
启用多个辅助线程来并行进行垃圾回收,缩短回收时间;

2. 增量垃圾回收
垃圾回收和代码执行交替进行,减小阻塞,但是在gc停顿时,如何能够从暂停的地方继续遍历呢?Orinoco主要采用了下面两个方法:

  • 三色标记法: 常规标记标记清除法只有两种状态:活动对象(黑色)、未活动对象(白色)。三色标记有三种状态:活动对象(黑色)、已经访问过并正在处理的对象(灰色)、未活动对象(白色)
  • 写屏障(强三色不变性): 当发生了黑色的节点引用了白色的节点,写屏障机制会强制将被引用的白色节点变成灰色的,这样就保证了下一次增量标记阶段可以正确标记;

3.并发垃圾回收
主线程在执行 JavaScript 的过程中,辅助线程能够在后台完成执行垃圾回收的操作。可以理解为异步的垃圾回收策略,同时为了解决增量标记的问题,也需要进行写屏障操作。

4.惰性清理:
在增量标记之后,如果剩余的内存空间足以让JS代码跑起来,就会延迟清理,先让JS代码执行,或者只清理部分垃圾,而不清理全部。

🪴九、内存泄漏

内存泄漏指的是程序中未释放不再需要的内存的情况。

(一)常见可能造成内存泄漏的问题:

  • 不合理的闭包
  • 被遗忘的定时器(setInterval)、requestAnimationFrame;
  • DOM 引用丢失(DOM 被移除了,但是绑定在其身上的各类事件还在);
  • 比如一些音视频的播放器,在不使用时,要及时的 destory 而非简单的移除其 DOM 元素;
  • 未清理的console打印;
  • 被忽视的全局变量引用;
  • 第三方库的引用与销毁;
  • ES6 的一些语法的使用:Map、Set 等。要及时清除引用或者使用弱引用的weakmap、weakset,例如下:
const mySet = new Set();
const obj = { key: 'value' };
mySet.add(obj);
// 在不再需要 obj 时,手动删除它
mySet.delete(obj);

const weakSet = new WeakSet();
const obj = { key: 'value' };
weakSet.add(obj);
// 不再需要 obj 时,WeakSet 会自动处理

(二)内存泄漏问题定位

利用 Chrome 的 DevTools 可以很容易排查内存泄漏问题。主要是用 Performance性能 面板和 Memory内存 面板。

1. 利用 Performance 工具排查是否存在内存泄漏问题

打开 Performance 面板,勾选内存选项(默认是不勾选的),即可开始收集内存随时间的变化曲线,如下图中框选的蓝色趋势部分,如果该趋势走向趋于平稳,则内存回收正常,否则即可能存在内存泄漏问题。

内存选项旁边的扫帚图标可以手动进行GC(垃圾回收)

在这里插入图片描述

2. 利用 Memory 面板定位问题

Memory 面板有三个选项:堆快照(Heap Snapshot)、内存时间轴(Memory Timeline)、内存分配采样(Allocation)

一般常用堆快照和内存时间轴。
堆快照可以捕获网页的内存快照,并提供详细的内存信息和统计数据。如果你已经大概猜到了哪里导致了内存泄漏,可以在操作前后捕获内存快照,并进行比较,在增量 > 0点记录中定位问题。

在这里插入图片描述

而内存时间轴可以显示网页在时间轴上的内存使用情况,在时间轴上,可以看到有起伏的蓝色和灰色柱状图,其中蓝色代表当前时间线下所占用的内存;灰色表示表示原占用空间得到释放。
录制一段时间之后,结束录制,同样会生成快照。比堆快照更方便的是,你可以查看各个时间段的内存数据以排查问题。

在这里插入图片描述

当然你也可以查看最终的内存分配情况定位问题:

在这里插入图片描述

3. 需要关注的Constructor构造函数

可以看到,在内存面板中,堆内存列表列出了很多构造函数,为了快速定位问题,你需要了解下这些常见的构造函数大致代表什么:

  • system、system/Context 表示引擎自己创建的以及上下文创建的一些引用,这些不用太关注,不重要
  • closure 表示一些函数闭包中的对象引用
  • array、string、number、regexp 这一系列也能看出,就是引用了数组、字符串、数字或正则表达式的对象类型
  • HTMLDivElement、HTMLAnchorElement、DocumentFragment等等这些其实就是你的代码中对元素的引用或者指定的 DOM 对象引用

🎁 说在最后

学如逆水行舟,不进则退~加油吧少年👊👊👊

先看后赞,养成习惯👍
收藏吃灰,不如学会🍗
点个关注,不要迷路🪤

  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端阿彬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值