JavaScript 系列之:垃圾回收机制

前言

垃圾回收是一种自动内存管理机制,用于检测和清除不再使用的对象,以释放内存空间。当一个对象不再被引用时,垃圾回收器会将其标记为垃圾,然后在适当的时候清除这些垃圾对象,并将内存回收给系统以供其他对象使用。

什么是"对象不再被引用"?

当一个对象不再被任何变量或属性引用时,它就成为垃圾。例如,当一个函数执行完毕后,其中创建的局部变量将成为垃圾,因为它们无法再被访问到。

"适当的时候"是指什么时候?

首先 JavaScript 引擎会定期找出垃圾,也会在内存使用量到达某一阈值或内存分配失败时去找垃圾,还有一些其他的机制这里不做拓展。

JavaScript 引擎是如何找到并清理垃圾的?

有两种方式:标记清除法和引用计数法。现在大都用的是标记清除法,因为引用计数法在处理循环引用时存在问题。

对于栈中的垃圾和堆中的垃圾,清理时有什么区别?

当我们在 JavaScript 中讨论垃圾回收机制时,主要关注的是堆内存中的回收机制。栈内存的管理是由编译器和解释器自动进行的,当执行上下文被销毁时,其所占用的栈内存会自动被回收。

栈中的垃圾清理堆中的垃圾清理
存储内容基本类型数据、函数调用上下文复杂数据结构(对象和数组)
管理方式自动管理,由编译器和解释器负责依赖垃圾回收机制,自动回收不再被引用的对象
垃圾清理方式无需显式清理,执行上下文销毁时自动释放内存通过垃圾回收机制周期性检查并回收不再被引用的对象内存

标记清除

它分为两个阶段:标记阶段和清除阶段。

  1. 标记阶段: 在标记阶段,垃圾回收器会对内存中的所有对象进行遍历,从根对象开始(通常是全局对象)递归地遍历对象的引用关系。对于每个被访问到的对象,垃圾回收器会给它打上标记,表示该对象是可达的,即不是垃圾。这个过程确保了所有可达对象都会被标记。

  2. 清除阶段: 在清除阶段,垃圾回收器会遍历整个内存,对于没有标记的对象,即被判定为垃圾的对象,会被立即回收,释放内存空间。这样,只有被标记的对象会被保留在内存中,而垃圾对象会被清除。
    在这里插入图片描述
    上图中蓝色的元素代表被访问到的对象,即可达对象,灰色代表没有被访问到的对象,即不可达对象

如何理解"递归地遍历对象的引用关系"?

在 JavaScript 中,内存中的对象通过引用关系相互连接,形成了复杂的引用图。

当 JavaScript 代码执行时,会创建各种对象,包括全局对象、函数对象、数组对象、普通对象等等。这些对象在内存中占据一定的空间。

随着程序的执行,对象之间会建立引用关系。这种引用关系通常是通过将一个对象的引用赋值给另一个对象的属性、局部变量或函数的参数来实现的。

例如你在全局作用域中写了这行代码 var obj = {},这就代表着你给全局对象设置了一个 obj 属性;再例如一个函数将一个对象作为参数,实际上传递的是对象的引用…这些都会成为引用关系的一部分。通过这些引用关系,JavaScript 引擎会动态地构建一个复杂的引用图。这个引用图以根对象为起点,通过对象之间的引用关系不断扩展和延伸。

标记阶段完成之后,如果其中一个变量不再被引用了,此时该变量还是被标记状态的,那么清除阶段怎么识别它呢?

在本次清除阶段识别不了,但是在下一轮的标记阶段,该变量不会被打上标记,那它就可以被清除啦。

如果一个变量在标记阶段没有被引用,它没有被打上标记,但是在清除阶段之前,它又被引用了,那它会被视作垃圾回收吗?

无需担心,现代垃圾回收器通常会使用各种策略(如写屏障、增量标记、并发标记等)来跟踪引用变化,不会错误地回收仍然被引用的对象。

如何理解根对象?

根对象通常包括以下几类:

  1. 全局对象:在浏览器中全局对象是 window(Node.js 中是 global),它包含了全局作用域中定义的所有属性和方法。全局对象总是可达的,因为它是程序的入口点之一,这也意味着全局对象永远不会被垃圾回收机制视为垃圾对象来回收。

  2. 活跃的执行上下文(Execution Contexts) :可以理解为当前正在执行或即将执行的代码块所对应的执行上下文。"活跃的执行上下文"可以是全局执行上下文,也可以是某个函数执行上下文,具体取决于当前执行的代码块。全局对象其实就是全局执行上下文中的一个组成部分。

  3. 内置的持久性引用:有些 JavaScript 引擎可能会维护一些内置的、持久的引用,这些引用指向的对象也会被视为根对象。例如,一些引擎可能会将当前执行的栈帧或特定类型的内部数据结构保留在内存中,以便进行调试或性能分析。

引用计数

它的策略是每个对象维护一个计数器来记录其被引用的次数,当对象被新引用时计数加 1,当引用被移除时计数减 1,当计数减至 0 时对象被自动回收。

两者优缺点比较

标记清除:

  • 优点:

    • 简单有效

    • 可以处理循环引用

  • 缺点:

    • 标记清除算法会暂停程序的执行,也就是说垃圾回收过程中会停顿

    • 标记清除算法会在回收过程中产生大量的不连续的、碎片化的内存空间,从而使得内存的利用率降低。

引用计数:

  • 优点:

    • 简单高效

    • 实时回收:用计数可以在对象不再被引用时立即回收,不需要等待垃圾收集器的运行。

  • 缺点:

    • 循环引用:当两个或多个对象相互引用时,它们的引用计数都不为零,即它们无法被回收,这会导致内存泄漏

哪些操作会导致内存泄漏?

全局变量导致内存泄漏

如果意外地将变量声明为全局变量(例如,在函数外部或在函数内部但未使用 varletconst 声明的变量),那么这些变量在程序的整个生命周期内都将保持活动状态,可能导致内存泄漏。

function myFunction() {  a = "I am a global variable now!"; // 忘记使用 var, let 或 const
}  myFunction();  
// 此时 a 成为了全局变量,即使函数执行完毕,a 也不会被回收

闭包

function createLeakyClosure() {var localVar = "我是局部变量,但由于闭包,我可能导致内存泄漏";// 返回一个匿名函数,该函数引用了localVarreturn function() {return localVar;};
}// 外部引用闭包
var closure = createLeakyClosure();// 如果 closure 不再需要,但由于它是闭包,它仍然持有对 localVar 的引用,
// 这可能导致 localVar 占用的内存无法被回收,直到 closure 被明确置为 null 或 undefined。

DOM 引用未释放

如果一个 DOM 元素被从 DOM 树中移除,但仍有 JavaScript 变量或对象保持对该 DOM 元素的引用,那么这块 DOM 元素占用的内存(包括其属性、事件监听器等)将不会被垃圾回收机制回收,从而造成了内存泄漏。

例如有一个长时间存活的对象(如全局对象),它有一个属性的值是该 DOM 元素,即使你通过操作 DOM 树删除了该 DOM 元素,该 DOM 元素所占据的内存空间不会被视作垃圾回收,直到这个长时间存活的对象被销毁。

定时器/计时器

没有清除定时器,导致计时器持续运行,占用内存空间,导致内存泄漏。尤其是在 vue 中,最好是在 beforeDestory() 中清除定时器。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(七)趣学设计模式 之 适配器模式!

目录 一、 啥是适配器模式?二、 为什么要用适配器模式?三、 适配器模式的实现方式1. 类适配器模式(继承插座 👨‍👩‍👧‍👦)2. 对象适配器模式(插座转换器 &#x1f50c…

【Java基础】Java中new一个对象时,JVM到底做了什么?

Java中new一个对象时,JVM到底做了什么? 在Java编程中,new关键字是我们创建对象的最常用方式。但你是否想过,当你写下new MyClass()时,Java虚拟机(JVM)到底在背后做了哪些工作?今天&…

内网穿透:打破网络限制的利器

目录 深入理解内网穿透 内网与外网的奥秘 内网穿透的原理剖析 总结与展望 在如今这个数字化时代,网络已经成为我们生活和工作中不可或缺的一部分。但你是否遇到过这样的困扰:在家办公时,想要访问公司内部的文件服务器,却因为网…

【汽车ECU电控数据管理篇】HEX文件格式解析篇章

一、HEX格式文件是啥 HEX 文件是 Intel 公司提出的一种按地址排列的数据信息格式,通常用于存储嵌入式系统的二进制代码。它以 ASCII 码的形式记录数据,每一行以冒号开头,包含数据长度、地址、记录类型、数据和校验码等信息。HEX 文件常用于程…

深入理解 CSS pointer-events: none:穿透点击的魔法

一、什么是 pointer-events: none? pointer-events: none 是一个强大的 CSS 属性,它控制元素是否响应鼠标/触摸事件(如点击、悬停、拖拽)。当设置为 none 时,元素会变得“透明”,事件会直接穿透到下方的元…

【AHK】资源管理器自动化办公实例/自动连点设置

此处为一个自动连续点击打开检查的自动化操作案例,没有quicker的鼠键录制,不常用了,做个备份 #MaxThreadsPerHotkey 2 ; 这个是核心!!!!确保可以同时运行多个热键或标签global isRunning : tru…

html css js网页制作成品——HTML+CSS甜品店网页设计(5页)附源码

目录 一、👨‍🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨‍&#x1f…

Springboot使用Milvus的基本操作

Milvus 先得保证数据的正确安装并且正确运行 <dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId> </dependency> <dependency><groupId>io.milvus</groupId><artifactId>milvu…

初阶数据结构(C语言实现)——3顺序表和链表(2)

2.3 数组相关面试题 原地移除数组中所有的元素val&#xff0c;要求时间复杂度为O(N)&#xff0c;空间复杂度为O(1)。OJ链接 力扣OJ链接-移除元素删除排序数组中的重复项。力扣OJ链接-删除有序数组中的重复项合并两个有序数组。力扣OJ链接-合并两个有序数组 2.3.1 移除元素 1…

【力扣】2619. 数组原型对象的最后一个元素——认识原型与原型链

【力扣】2619. 数组原型对象的最后一个元素——认识原型与原型链 文章目录 【力扣】2619. 数组原型对象的最后一个元素——认识原型与原型链题目解决方案概述全局上下文函数上下文事件处理程序构造函数上下文类上下文显式 / 隐式绑定绑定方法和永久 this 上下文 方法 1&#xf…

ubuntu终端指令集 shell编程基础(一)

磁盘指令 连接与查看&#xff1a;磁盘与 Ubuntu 有两种连接方式&#xff1b;使用ls /dev/sd*查看是否连接成功&#xff0c;通过df系列指令查看磁盘使用信息。若 U 盘已挂载&#xff0c;相关操作可能失败&#xff0c;需用umount取消挂载。磁盘操作&#xff1a;使用sudo fdisk 磁…

基于Spark的电商供应链系统的设计与实现

目录 1.研究背景与意义 2、国内外研究现状 3、相关理论与技术 &#xff08;一&#xff09;分布式计算系统Spark &#xff08;二&#xff09;数据仓库Hive &#xff08;三&#xff09;读取服务器本地磁盘的日志数据Flume &#xff08;四&#xff09;分布式消息队列Kafka …

使用TortoiseGit配合BeyondCompare实现在Git仓库中比对二进制文件

使用TortoiseGit的比对工具可以直接右键&#xff0c;点击选择比对和上一版本的变化差异&#xff1a; 但是TortoiseGit只能支持比对纯文本文件的变化差异&#xff0c;如果尝试比对二进制文件&#xff0c;会提示这不是一个有效的文本文件&#xff1a; BeyondCompare可以比对二进制…

BladeX框架接口请求跨域

前端使用代理请求接口&#xff0c;接口可以正常访问。如果换全路径请求就跨域。 除了后端要配置跨域 还需要修改配置文件对OPTIONS请求的限制

Vue.js响应式基础

响应式基础​ API 参考 本页和后面很多页面中都分别包含了选项式 API 和组合式 API 的示例代码。现在你选择的是 组合式 API。你可以使用左侧侧边栏顶部的“API 风格偏好”开关在 API 风格之间切换。 声明响应式状态​ ref()​ 在组合式 API 中,推荐使用 ref() 函数来声明…

选开源CMS建站系统时,插件越多越好吗?

在选择开源CMS建站系统时&#xff0c;插件数量并不是唯一的衡量标准&#xff0c;更不能简单地说“插件越多就越好”&#xff0c;还是需要综合评估来考虑选择结果&#xff0c;以下是有关选择开源CMS系统时对插件数量的考量。 插件数量的优势插件数量可能带来的问题功能丰富性&a…

在VSCode中使用MarsCode AI最新版本详解

如何在VSCode中使用MarsCode AI&#xff1a;最新版本详解与使用场景 在当今快速发展的软件开发领域&#xff0c;人工智能&#xff08;AI&#xff09;技术的应用已经变得越来越普遍。ByteDance推出的MarsCode AI是一款强大的AI编程助手&#xff0c;旨在帮助开发者更高效地编写代…

mac修改docker的daemon.json 镜像文件

1、找到daemon.json文件的位置 docker info 可以看出位置在&#xff1a; /Users/spuer/.docker 2. 进入daemon.json 所在的目录&#xff1a; cd /Users/spuer/.docker3. 查看daemon.json的内容&#xff1a; more daemon.json可以看出&#xff0c;没有配置registry-mirrors&…

5.10 P-Tuning v2:多层级提示编码的微调革新

P-Tuning v2:多层级提示编码的微调革新 一、技术架构解析 #mermaid-svg-4Wy6vkXZi67hY9PZ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-4Wy6vkXZi67hY9PZ .error-icon{fill:#552222;}#mermaid-svg-4Wy6vkXZi67h…

Eclipse 编译项目指南

Eclipse 编译项目指南 引言 Eclipse 是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;广泛用于Java、C/C、Python等多种编程语言的开发。在Eclipse中编译项目是进行软件开发的基础步骤。本文将详细介绍如何在Eclipse中编译项目&#xff0c;包括项目设置…