JavaScript进阶(十五):JS 垃圾回收机制_vue gc

  • 内存:由可读写单元组成,表示一片可操作空间;
  • 管理:人为的去操作一片空间的申请、使用和释放;
  • 内存管理:开发者主动申请空间、使用空间、释放空间;
  • 管理流程:申请-使用-释放;

部分语言(例如C语言)需要手动去释放内存,但是会很麻烦,所以很多语言,例如JAVA都会提供自动内存管理机制,称为“垃圾回收机制”,JavaScript也提供了垃圾回收机制(Garbage Collecation),简称GC机制

三、全停顿(Stop The World )

在介绍垃圾回收算法之前,我们先了解一下「全停顿」。垃圾回收算法在执行前,需要将应用逻辑暂停,执行完垃圾回收后再执行应用逻辑,这种行为称为 「全停顿」(Stop The World)。例如,如果一次GC需要50ms,应用逻辑就会暂停50ms。

全停顿的目的,是为了解决应用逻辑与垃圾回收器看到的情况不一致的问题。

举个例子,在自助餐厅吃饭,高高兴兴地取完食物回来时,结果发现自己餐具被服务员收走了。这里,服务员好比垃圾回收器,餐具就像是分配的对象,我们就是应用逻辑。在我们看来,只是将餐具临时放在桌上,但是服务员看来觉得你已经不需要使用了,因此就收走了。你与服务员对于同一个事物看到的情况不一致,导致服务员做了与我们期望违背的事情。因此,为避免应用逻辑与垃圾回收器看到的情况不一致,垃圾回收算法在执行时,需要停止应用逻辑。

3.1 JavaScript 中的垃圾回收

JavaScript中内存会被判定为垃圾的情形如下:

  • 对象不再被引用;
  • 对象不能从根上访问到;

常见的GC算法如下:

  • 引用计数
  • 标记清除
  • 标记整理
  • 分代回收
3.1.1 引用计数

早期的浏览器最常使用的垃圾回收方法叫做"引用计数"(reference counting):语言引擎有一张"引用表",保存了内存里面所有资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。

const user1 = {age: 11}
const user2 = {age: 22}
const user3 = {age: 33}

const userList = [user1.age, user2.age, user3.age]

上面这段代码,当执行过一遍后,user1、user2、user3都是被userList引用的,所以它们的引用计数不为零,就不会被回收

function fn() {
    const num1 = 1
    const num2 = 2
}

fn()

上面代码中fn函数执行完毕,num1、num2都是局部变量,执行过后,它们的引用计数就都为零,所有这样的代码就会被当做“垃圾”,进行回收。

引用计数算法有一个比较大的问题: 循环引用

function objGroup(obj1, obj2) {
    obj1.next = obj2
    obj2.prev = obj1

    return {
        o1: obj1,
        o2: obj2,
    }
}

let obj = objGroup({name: 'obj1'}, {name: 'obj2'})
console.log(obj)

上面的这个例子中,obj1和obj2通过各自的属性相互引用,所有它们的引用计数都不为零,这样就不会被垃圾回收机制回收,造成内存浪费。

引用计数算法其实还有一个比较大的缺点,就是我们需要单独拿出一片空间去维护每个变量的引用计数,这对于比较大的程序,空间开销还是比较大的。

引用计数算法优点:

  • 引用计数为零时,发现垃圾立即回收;
  • 最大限度减少程序暂停;

引用计数算法缺点:

  • 无法回收循环引用的对象;
  • 空间开销比较大;
3.1.2 标记清除(Mark-Sweep)

核心思想:分标记清除两个阶段完成。

  1. 遍历所有对象找标记活动对象;
  2. 遍历所有对象清除没有标记对象;
  3. 回收相应的空间。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
标记清除算法的优点是:对比引用计数算法,标记清除算法最大的优点是能够回收循环引用的对象,它也是v8引擎使用最多的算法。

标记清除算法的缺点是:
在这里插入图片描述

上图我们可以看到,红色区域是一个根对象,就是一个全局变量,会被标记;而蓝色区域就是没有被标记的对象,会被回收机制回收。这时就会出现一个问题,表面上蓝色区域被回收了三个空间,但是这三个空间是不连续的,当我们有一个需要三个空间的对象,那么我们刚刚被回收的空间是不能被分配的,这就是“空间碎片化”。

3.1.3 标记整理(Mark-Compact)

为了解决内存碎片化的问题,提高对内存的利用,引入了标记整理算法。

标记整理可以看做是标记清除的增强。标记阶段的操作和标记清除一致。

清除阶段会先执行整理,移动对象位置,将存活的对象移动到一边,然后再清理端边界外的内存。

标记整理的缺点是:移动对象位置,不会立即回收对象,回收的效率比较慢。

3.1.4 增量标记(Incremental Marking)

为了减少全停顿的时间,V8对标记进行了优化,将一次停顿进行的标记过程,分成了很多小步。每执行完一小步就让应用逻辑执行一会儿,这样交替多次后完成标记。
在这里插入图片描述

长时间的GC,会导致应用暂停和无响应,将会导致糟糕的用户体验。从2011年起,v8就将「全暂停」标记换成了增量标记。改进后的标记方式,最大停顿时间减少到原来的1/6。

四、v8 引擎垃圾回收策略

  • 采用分代回收的思想;
  • 内存分为新生代、老生代;
    在这里插入图片描述

针对不同对象采用不同算法:

  • 新生代:对象的存活时间较短。新生对象或只经过一次垃圾回收的对象。
  • 老生代:对象存活时间较长。经历过一次或多次垃圾回收的对象。

在这里插入图片描述
V8堆的空间等于新生代空间加上老生代空间。且针对不同的操作系统对空间做了内存限制。

类型 \ 系统位数64位32位
老生代1400MB700MB
新生代32MB700MB

限制内存的原因:

  • 针对浏览器来说,这样的内存是足够使用的。
  • 针对浏览器的GC机制,经过不断的测试,如果内存再设置大一点,GC回收的时间就会达到用户的感知,会造成感知上的卡顿。
4.1 回收新生代对象

回收新生代对象主要采用复制算法Scavenge 算法)加标记整理算法。而Scavenge 算法的具体实现,主要采用了Cheney算法

Cheney算法将内存分为两个等大空间,使用空间为From,空闲空间为To

检查From空间内的存活对象,若对象存活,检查对象是否符合晋升条件,若符合条件则晋升到老生代,否则将对象从 From 空间复制到 To 空间。若对象不存活,则释放不存活对象的空间。完成复制后,将 From 空间与 To 空间进行角色翻转。

4.1.1 对象晋升机制

一轮GC还存活的新生代需要晋升。
当对象从From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。设置为25%的比例的原因是,当完成 Scavenge 回收后,To 空间将翻转成From 空间,继续进行对象内存的分配。若占比过大,将影响后续内存分配。

4.2 回收老生代对象

回收老生代对象主要采用标记清除标记整理增量标记算法,主要使用标记清除算法,只有在内存分配不足时,采用标记整理算法。

  1. 首先使用标记清除完成垃圾空间的回收
  2. 采用标记整理进行空间优化
  3. 采用增量标记进行效率优化
4.2.1 新生代和老生代回收对比
  • 新生代由于占用空间比较少,采用空间换时间机制。
  • 老生代区域空间比较大,不太适合大量的复制算法和标记整理,所以最常用的是标记清除算法,为了就是让全停顿的时间尽量减少。
4.3 内存泄漏识别方法

我们先写一段比较消耗内存的代码:

<button class="btn">点击</button>

<script>
    const btn = document.querySelector('.btn')
    const arrList = []

    btn.onclick = function() {
        for(let i = 0; i < 100000; i++) {
            const p = document.createElement('p')
            // p.innerHTML = '我是一个p元素'
            document.body.appendChild(p)
        }

        arrList.push(new Array(1000000).join('x'))
    }
</script>

使用浏览器的Performance来监控内存变化
在这里插入图片描述
点击录制,然后我们操作消耗性能的操作,操作完成之后,点击stop停止录制。
在这里插入图片描述
然后我们看一看是哪些地方引起了内存的泄漏,我们只需要关注内存即可。
在这里插入图片描述

可以看到内存在短时间内消耗的比较快,下降的小凹槽,就是浏览器在进行垃圾回收。

五、性能优化

5.1 避免使用全局变量
  • 全局变量会挂载在window下;
  • 全局变量至少有一个引用计数;
  • 全局变量存活更久,持续占用内存;
  • 在明确数据作用域的情况下,尽量使用局部变量;

最后

全网独播-价值千万金融项目前端架构实战

从两道网易面试题-分析JavaScript底层机制

RESTful架构在Nodejs下的最佳实践

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

一线互联网企业如何初始化项目-做一个自己的vue-cli

思维无价,看我用Nodejs实现MVC

代码优雅的秘诀-用观察者模式深度解耦模块

前端高级实战,如何封装属于自己的JS库

VUE组件库级组件封装-高复用弹窗组件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/577627.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

社交巨头与去中心化:解析Facebook在区块链的角色

在数字化时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。作为全球最大的社交媒体平台&#xff0c;Facebook 在社交领域的影响力无可置疑。然而&#xff0c;随着区块链技术的崛起&#xff0c;Facebook 也开始探索如何将这一技术应用于其平台&#xff0c;以适…

基于LSTM算法实现交通流量预测(Pytorch版)

算法介绍 LSTM&#xff08;Long Short-Term Memory&#xff09;算法是一种特殊设计的循环神经网络&#xff08;RNN, Recurrent Neural Network&#xff09;&#xff0c;专为有效地处理和建模序列数据中的长期依赖关系而开发。由于传统RNN在处理长序列时容易遇到梯度消失和梯度…

ElasticSearch语句中must,must_not,should 组合关系

前言&#xff1a; 在实际应用中&#xff0c;发现当bool中同时使用must和should 没有达到想要的想过&#xff0c;而是只展示了must中的命中数据&#xff0c;所以打算探究一下bool中 三种逻辑关系的组合。 上述查询语句只展示了must的结果&#xff0c;没有should中的结果&#…

本地Windows主机,使用pycharm通过wsl的ubuntu来创建django项目

Windows主机在pycharm中通过wsl的ubuntu来创建django项目 需求&#xff1a;在windows主机中创建python项目再转接到linux服务器中运行&#xff0c;有点麻烦。【特别是存放日志文件或其他文件路径时需要修改为linux中的路径】 1&#xff1a;我的是windows主机 2&#xff1a;有…

基于java+springboot+vue实现的个人博客系统(文末源码+Lw)200

摘 要 随着国内市场经济这几十年来的蓬勃发展&#xff0c;突然遇到了从国外传入国内的互联网技术&#xff0c;互联网产业从开始的群众不信任&#xff0c;到现在的离不开&#xff0c;中间经历了很多挫折。本次开发的个人博客系统&#xff0c;有管理员&#xff0c;用户&#xf…

神经网络参数初始化

&#x1f4bd;参数初始化是神经网络训练过程中的一个重要步骤。在构建神经网络时&#xff0c;我们需要为权重和偏置等参数赋予初始值。对于偏置&#xff0c;通常可以将其初始化为0或者较小的随机数。然而&#xff0c;对于权重w的初始化&#xff0c;我们通常会采用更加复杂的方法…

【论文笔记 | 异步联邦】PORT:How Asynchronous can Federated Learning Be?

1. 论文信息 How Asynchronous can Federated Learning Be?2022 IEEE/ACM 30th International Symposium on Quality of Service (IWQoS). IEEE, 2022&#xff0c;不属于ccf认定 2. introduction 2.1. 背景&#xff1a; 现有的异步FL文献中设计的启发式方法都只反映设计空…

《2024年绿色发展报告》:算力与电力矛盾愈加突出!

2024年4月22日&#xff0c;第55个世界地球日&#xff0c;超聚变发布《2024年绿色发展报告》&#xff0c;向社会展示超聚变面对宏观形势变化、产业趋势变化&#xff0c;推进绿色发展、科技向绿的探索与实践成果。 2023年&#xff0c;算力产业发生了深刻变化。大模型带来AI算力需…

小程序中如何快速给分类添加商品

​快速在分类下面上传商品&#xff0c;并且能够设置商品顺序&#xff0c;关系到运营效率的高低。下面就具体介绍如何快速在某个分类下面设置商品。 一、在商品管理处&#xff0c;查询某个分类下面的商品。 进入小程序管理员后台->商品管理&#xff0c;点击分类输入框&…

从零开始利用MATLAB进行FPGA设计(五)详解双口RAM

创作于谱仪算法设计过程中的数字能谱生成模块设计。 往期回顾&#xff1a; 从零开始利用MATLAB进行FPGA设计&#xff08;四&#xff09;生成优化HDL代码 从零开始利用MATLAB进行FPGA设计&#xff08;三&#xff09;将Simulink模型转化为定点数据类型 目录 1.关于双口RAM …

大模型咨询培训老师叶梓:利用知识图谱和Llama-Index增强大模型应用

大模型&#xff08;LLMs&#xff09;在自然语言处理领域取得了显著成就&#xff0c;但它们有时会产生不准确或不一致的信息&#xff0c;这种现象被称为“幻觉”。为了提高LLMs的准确性和可靠性&#xff0c;可以借助外部知识源&#xff0c;如知识图谱。那么我们如何通过Llama-In…

Web前端开发之CSS_1

CSS选择器字体属性背景属性文本属性表格属性 1. CSS 1.1 CSS简介 CSS&#xff08;Cascading Style Sheets&#xff09;层叠样式表&#xff0c;又叫级联样式表&#xff0c;简称样式表。CSS文件后缀名为 .css 。CSS用于HTML文档中元素样式的定义。使用CSS可以让网页具有美观一致…

算法 || 二分查找

目录 二分查找 在排序数组中查找元素的第一个和最后一个位置 搜索插入位置 一个数组经过划分后具有二段性的都可以用二分查找 二分查找 704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; ​ 暴力解法&#xff1a;直接遍历数组&#xff0c;找到 target 便返回下标&am…

【blog项目】layui与jquery冲突导致鼠标悬停事件失效、如何调用layui.use()作用域里的方法

blog项目前台展示——查询数据库中的文章类型并展示时出现的bug 1 正常演示 2 用jquery查询数据库并添加到页面后 3 相关代码 <script src"/static/jquery-2.1.4.js"></script> <script src"/static/layui/layui.js"></script> …

排序算法-计数排序

一、计数排序 这种排序算法 是利用数组下标来确定元素的正确位置的。 如果数组中有20个随机整数&#xff0c;取值范围为0~10&#xff0c;要求用最快的速度把这20个整数从小到大进行排序。 很大的情况下&#xff0c;它的性能甚至快过那些时间复杂度为O(nlogn&#xff09;的排序。…

使用PyCharm开发工具创建工程

一. 简介 前面文章实现了开发 python程序使用的 开发工具PyCharm&#xff0c;本文来学习使用 PyCharm开发工具创建一个 python工程。 二. 使用PyCharm开发工具创建工程 1. 首先&#xff0c;打开 PyCharm开发工具&#xff0c;打开 "New project" 选项&#xff1a; …

git如何查询回退之前的提交记录

git如何查询回退之前的提交记录 使用 git reflog 命令&#xff1a; 使用 git reflog 命令&#xff1a; git refloggit reflog 显示的是你的本地引用日志&#xff0c;它包含了所有HEAD指向变更的历史记录&#xff0c;即使那些已经被删除的提交也会出现在这里。当你误操作回退并…

一款可视化正则表达式工具

regex-vis是一款在线免费且可视化的正则表达式工具 界面图&#xff1a; 只能输入由26个英文字母组成的字符串 ^[A-Za-z]$ 只能输入数字 ^[0-9]*$测试错误 测试正确 快来感受一下叭 官方网址&#xff1a; Regex VisRegex visualizer & editor, make the regular expr…

Java根据模板动态生成Pdf(添加页码、文件加密、Spire免费版本10页之后无法显示问题、嵌入图片添加公章、转Base64)

Java根据模板动态生成Pdf&#xff1a;添加页码、文件加密、Spire免费版本10页之后无法显示问题、嵌入图片添加公章、转Base64 引言【Java根据模板动态生成Pdf资源地址】示例一&#xff1a;动态生成带页码的PDF报告示例二&#xff1a;加密PDF以保护敏感信息示例三&#xff1a;应…

设计模式——终止模式之两阶段终止模式

文章目录 1. 错误思路2. 两阶段终止模式2.1 利用 isInterrupted2.2 利用停止标记interrupt-打断park Two Phase Termination 在一个线程 T1 中如何“优雅”终止线程 T2&#xff1f;这里的【优雅】指的是给 T2 一个料理后事的机会。 1. 错误思路 使用线程对象的 stop() 方法停…