1.垃圾回收的概念

1.1 什么是垃圾回收机制:

GCGarbage Collection ,程序工作过程中会产生很多"垃圾",这些垃圾是程序不用的内存或者是之前用过了,以后不会再用的内存空间,而 GC 就是负责回收垃圾的,因为他工作在引擎内部,所以对于我们前端来说,GC 过程是相对比较无感的,这一套引擎执行而对我们又相对无感的操作也就是常说的 垃圾回收机制
不是所有语言都有 GC,一般的高级语言里面会自带 GC,比如 Java、Python、JavaScript 等,也有无 GC 的语言,比如 C、C++ 等,那这种就需要我们程序员手动管理内存了,相对比较麻烦
在像 C/C++ 这样的语言中,开发者需要手动分配(malloc)和释放(free)内存。这种方式非常灵活,性能也高,但有两个致命缺点:

  • 忘记释放:会导致内存泄漏,程序占用的内存会随着时间推移越来越多,最终可能导致程序崩溃或系统变慢。
  • 提前释放:或释放了多次,会导致悬挂指针,当程序尝试访问一个已经被释放的内存地址时,会引发不可预知的错误(通常是程序崩溃)。

JavaScript中存在两种变量:局部变量全局变量。全局变量的生命周期会持续到页面卸载;而局部变量声明在函数中,它的生命周期从函数执行开始,直到函数执行结束,在这个过程中,局部变量会在堆或栈中存储它们的值,当函数执行结束后,这些局部变量不再被使用,它们所占有的空间就会被释放。(不过,当局部变量被外部函数使用时,其中一种情况就是闭包,在函数执行结束后,函数外部的变量依然指向函数内部的局部变量,此时局部变量依然在被使用,所以不会回收。)

2.垃圾回收机制是如何实现的

2.1核心理念:

GC 机制的核心思想是可达性。简单来说,就是判断一个对象是否“可达”,如果不可达,它就是“垃圾”。

  • 可达的对象:以某种方式被访问或使用的对象。
  • 不可达的对象:无法被访问到的对象,可以被安全地回收。

GC 会有一系列的对象,它们是可达性的起点。在 JavaScript 中,主要的根包括:

全局对象:比如浏览器环境下的 window 对象,Node.js 环境下的 global 对象。
因为全局对象在应用程序的整个生命周期内都存在。只要你的网页开着,window 对象就永远不会消失。它是所有全局变量和内置API(如 setTimeout, localStorage)的宿主。如果它被回收了,整个JavaScript环境就都将崩塌。因此,全局对象是GC最重要、最基础的一个“根”

函数调用栈:当前正在执行的函数中的局部变量和参数。
因为当前正在执行的代码和它所依赖的数据,理所当然是“存活”的。调用栈代表了程序执行的“此时此刻”。如果这些正在使用的变量被回收了,程序将立即出错。因此,调用栈中所有栈帧里的变量和参数,都被视为临时的“根”。

活跃的 DOM 树:页面上存在的 DOM 元素。
因为DOM节点是构成用户界面的实体。用户能看到、能与之交互的元素,必须始终存在于内存中,浏览器需要依据它来进行绘制和响应事件。因此,所有在DOM树上的节点都被认为是“可达的”。

垃圾回收器会从这些“根”出发,沿着引用链进行遍历。所有能从“根”访问到的对象,都会被认为是“活”的(可达的);反之,所有无法从“根”访问到的对象,就会被认为是“死”的(不可达的),并成为垃圾回收的目标。

// 创建一个根对象,并被变量 user 引用
let user = {name: "Alice"
};// 原来的 { name: "Alice" } 对象失去了引用,因此它变成了不可达对象,等待被回收。
user = null;
2.2主流垃圾回收算法

浏览器通常使用的垃圾回收方法有两种:标记清除引用计数

2.2.1标记清除

这是现代浏览器中最常用的垃圾回收算法。它完美地解决了循环引用的问题。

  • 原理:分为两个阶段:
    1. 标记阶段:垃圾回收器从“根”对象开始,遍历所有可达的对象,并在这些对象上打上一个“标记”,表示它们是存活的。
    2. 清除阶段:垃圾回收器遍历整个堆内存,所有没有被标记的对象都被视为垃圾,并被回收,其占用的内存被释放。
  • 优点:可以解决循环引用的问题。因为即使 objAobjB 互相引用,但如果它们都无法从“根”访问到,那么它们就都不会被标记,最终会被一起清除。
function createCircularReference() {let objA = {};let objB = {};objA.b = objB; objB.a = objA; 
}createCircularReference();
  • 缺点
    • 执行效率问题:GC 执行时,需要暂停整个程序的运行,如果堆内存很大,标记和清除会很耗时。
    • 内存碎片化:清除后,会产生大量不连续的内存碎片。如果之后需要分配一个大对象,可能会因为没有足够大的连续空间而失败。
2.2.2引用计数

这是早期的一种 GC 算法,思想非常简单。

  • 原理:为每个对象维护一个“引用计数器”。当有一个引用指向该对象时,计数器加1;当引用被移除时,计数器减1。当计数器变为0时,表示该对象不再被需要,可以被回收。
  • 优点:实现简单,垃圾可以被立即回收,不会有“暂停”的感觉。
  • 致命缺点:无法处理循环引用
    看下面的例子:

这种情况下,就要手动释放变量占用的内存:

obj1.a =  null
obj2.a =  null
2.3 V8 引擎的优化:分代回收

为了解决标记-清除算法的效率问题,Google 的 V8 引擎(用于 Chrome 和 Node.js)采用了一种更先进的策略:分代回收
这个策略基于一个重要的观察:“大部分对象都是朝生夕死的”。也就是说,很多对象在创建后很快就不再被使用,而少数对象会存活很长时间。

V8 将堆内存分为两个主要区域:
新生代:Scavenge 算法

  • 特点:存放生命周期短的对象,空间较小(通常为 1-8MB),垃圾回收频繁且速度快
  • 内部结构:新生代内存被平分为两个相等的空间:From 空间(使用中)To 空间(空闲)
  • **回收过程:
    1. 新对象首先被分配在 From 空间。
    2. 当 From 空间快要被占满时,触发一次新生代的 GC。
    3. GC 会检查 From 空间中的存活对象,并将它们复制到 To 空间。
    4. 复制完成后,From 空间剩下的所有对象都是垃圾。整个 From 空间被一次性清空。
    5. From 空间和 To 空间的角色互换,等待下一次 GC。
  • 晋升:如果一个对象在新生代中经过了多次 Scavenge 依然存活,那么它被认为是生命周期较长的对象,会被“晋升”到老生代中。此外,如果复制一个对象到 To 空间时,To 空间的使用率超过了25%,该对象也会被直接晋升到老生代。
    老生代:标记-清除与 标记-整理
  • 特点:存放生命周期长或体积大的对象,空间较大,GC 频率较低
  • 回收过程
    1. 主要使用标记-清除算法,流程如前所述。
    2. 为了解决内存碎片化问题,V8 引入了标记-整理算法。它在标记阶段之后,不是直接清除垃圾,而是将所有存活的对象向内存的一端移动,然后直接清理掉边界之外的所有内存。这样就得到了连续的空闲空间。

3.减少垃圾回收

3.1 手动处理:
虽然浏览器可以进行垃圾自动回收,但是当代码比较复杂时,垃圾回收所带来的代价比较大,所以应该尽量减少垃圾回收。

  • 数组进行优化: 在清空一个数组时,最简单的方法就是给其赋值为[ ],但是与此同时会创建一个新的空对象,可以将数组的长度设置为0,以此来达到清空数组的目的。
  • 对象进行优化: 对象尽量复用,对于不再使用的对象,就将其设置为null,尽快被回收。

避免意外的全局变量
始终使用 constlet 声明变量,开启严格模式('use strict';)。

function leakyFunction() {// 如果没有 'let' 或 'const', a 会被创建为全局变量// 它将永远不会被回收,除非手动设为 nulla = new BigObject();
}

警惕闭包
闭包是 JavaScript 的强大特性,但也很容易造成内存泄漏。闭包可以使其父函数中的变量在函数执行结束后仍然存活。

function createClosure() {let largeData = new Array(1e6).fill('*'); // 这个返回的函数持有了对 largeData 的引用return function() {...return largeData.length;};
}let myClosure = createClosure();
// 即使 createClosure 执行完毕,largeData 也不会被回收,因为它被 myClosure 引用。
// 如果不再需要它,应手动解除引用。
myClosure = null;

定时器和事件监听器
setInterval, setTimeoutaddEventListener 如果不被正确清理,它们的回调函数和其引用的外部变量都不会被回收。

let element = document.getElementById('my-button');
let largeData = new BigObject();function onClick() {// do something with largeData
}element.addEventListener('click', onClick);// 正确做法:
// element.removeEventListener('click', onClick);
// element = null;

在组件销毁或元素移除时,务必使用 clearInterval, clearTimeoutremoveEventListener 清理掉相关的定时器和监听器。

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

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

相关文章

工业相机选择规则

一、相机分辨率选择相机分辨率指的是相机传感器捕捉图像细节的能力,具体来说就是传感器上有效像素的总数量。可以把它理解为构成数字图像的“小方块”(像素)有多少个。工业领域内相机的分辨率的选择根据更具产品需要的精度要求和产品大小来确…

【Web安全】csrf、ssrf和xxe的区别

CSRF、SSRF 和 XXE 是三种不同类型的网络安全漏洞,它们的原理、攻击目标、利用方式和危害场景均有显著区别。以下从核心定义、原理、场景等维度详细对比三者的差异。一、核心定义与原理对比漏洞类型全称核心定义核心原理CSRF跨站请求伪造攻击者诱导用户在已登录的情…

【Lua】XLua一键构建工具

将以下代码放入Editor文件夹&#xff0c;点击菜单栏的XLua/一键生成代码和热补丁 即可。using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine;/// <summary> /// XLua自动化构建工具 //…

20250808:EasyGBS 对接大华 ICC 平台问题处理

最近有个现场在对接大华 ICC 平台时&#xff0c;客户反馈&#xff1a;EasyGBS 级联成功&#xff0c;但 ICC 显示下级离线。EasyGBS 成功对接过很多家国标平台&#xff0c;但这种情况确实少见。我们远程过去确认配置无误后&#xff0c;就进行了抓包&#xff0c;拿到包我就纳闷了…

js使用webscoket时使用自定义二进制包协议时并发问题处理

this.server new WebSocket.Server({ port: this.port });this.server.on(connection, (ws, req) > {const uniqueId dataUtil.uuid();ws.id uniqueId;global.serverSession.set(uniqueId, ws);logger.debug({ message: 客户端已连接, traceId: ws.id, address: req.sock…

元数据管理与数据治理平台:Apache Atlas 分类传播 Classification Propagation

文中内容仅限技术学习与代码实践参考&#xff0c;市场存在不确定性&#xff0c;技术分析需谨慎验证&#xff0c;不构成任何投资建议。Apache Atlas 框架是一套可扩展的核心基础治理服务&#xff0c;使企业能够有效、高效地满足 Hadoop 中的合规性要求&#xff0c;并支持与整个企…

TSF应用开发与运维部署

架构演进历程&#xff1a;单体架构-->SOA架构-->微服务架构-->Service Mesh腾讯微服务平台TSF (Tencent Service Framework) 是一个围绕应用和微服务的 PaaS 平台。提供服务全生命周期管理能力和数据化运营支持。提供多维度应用、服务、机器的监控数据&#xff0c;助力…

linux开发之mmap内存映射

mmap概念 mmp是 将文件或设备直接映射到进程的虚拟内存空间 的一种机制&#xff0c;可实现程序像访问内存一样访问文件&#xff0c;而不需要传统的 read()/write()系统调用 文件内容被映射到进程的地址空间&#xff0c;读写文件就像操作内存一样&#xff0c;操作系统负责自动同…

CPP继承

继承 一、继承概述 1、为什么需要继承 如下示例&#xff0c;Person 类、Student 类、Teacher 类有大量重复的代码&#xff0c;造成代码冗余&#xff0c;降低开发效率。我们可以通过继承来解决这一问题。在面向对象的编程语言中&#xff0c;继承是一个核心概念。主要作用将重复的…

模块 PCB 技术在未来通信领域的创新突破方向

未来通信领域对数据传输速率、信号稳定性及设备集成度的要求持续攀升&#xff0c;模块 PCB 作为通信设备的关键组件&#xff0c;其技术创新成为推动行业发展的核心动力。猎板 PCB 凭借深厚的技术积累与持续的研发投入&#xff0c;在模块 PCB 技术创新方面取得诸多突破&#xff…

mysql的InnoDB索引总结

MySQL InnoDB索引知识点总结 1. 索引类型 1.1 聚簇索引&#xff08;Clustered Index&#xff09; 定义与特性 定义&#xff1a;聚簇索引是InnoDB的默认存储方式&#xff0c;数据行按照主键的顺序物理存储在磁盘上特性&#xff1a; 每个InnoDB表只能有一个聚簇索引数据页中的记录…

C++模板的补充

类模板(上一篇没讲到类模板C/C内存管理&函数模板-CSDN博客&#xff09; 类模板的定义&#xff1a; template<class T1, class T2, ..., class Tn> class 类模板名 {// 类内成员定义 }; 用一个简单的栈例子讲类模板 #define _CRT_SECURE_NO_WARNINGS #include &l…

用JOIN替代子查询的查询性能优化

一、子查询的性能瓶颈分析‌重复执行成本‌关联子查询会导致外层每行数据触发一次子查询&#xff0c;时间复杂度为O(M*N)sql-- 典型低效案例 SELECT e.employee_id, (SELECT d.department_name FROM departments d WHERE d.department_id e.department_id) FROM employees e; …

【设计模式】访问者模式模式

访问者模式&#xff08;Visitor Pattern&#xff09;详解一、访问者模式简介 访问者模式&#xff08;Visitor Pattern&#xff09; 是一种 行为型设计模式&#xff08;对象行为型模式&#xff09;&#xff0c;它允许你在不修改对象结构的前提下&#xff0c;为对象结构中的元素添…

比特币现货和比特币合约的区别与联系

一、基本定义项目现货&#xff08;Spot&#xff09;合约&#xff08;Futures / Perpetual&#xff09;本质直接买卖比特币本身买卖比特币价格的衍生品合约所得资产真实的 BTC合约头寸&#xff08;没有直接持有 BTC&#xff09;结算方式交割比特币现金结算&#xff08;多数平台&…

Qt/C++开发监控GB28181系统/实时监测设备在线离线/视频预览自动重连/重新点播取流/低延迟

一、前言说明 一个好的视频监控系统&#xff0c;设备掉线后能够自动重连&#xff0c;也是一个重要的功能指标&#xff0c;如果监控系统只是个rtsp流地址&#xff0c;那非常好办&#xff0c;只需要重新打开流地址即可&#xff0c;而gb28181中就变得复杂了很多&#xff0c;需要多…

此芯p1开发板使用OpenHarmony时llama.cpp不同优化速度对比(GPU vs CPU)

硬件环境 Cix P1 SoC 瑞莎星睿 O6 开发板 rx580显卡 产品介绍&#xff1a; https://docs.radxa.com/orion/o6/getting-started/introduction OpenHarmony 5.0.0 使用vulkan后端的llama.cpp &#xff08;GPU&#xff09; # ./llama-bench -m /data/qwen1_5-0_5b-chat-q2_k.…

Android 四大布局:使用方式与性能优化原理

一、四大布局基本用法与特点1. LinearLayout&#xff08;线性布局&#xff09;使用方式&#xff1a;<LinearLayoutandroid:orientation"vertical" <!-- 排列方向&#xff1a;vertical/horizontal -->android:layout_width"match_parent"android:…

Redis的BigKey问题

Redis的BigKey问题 什么是大Key问题&#xff1f; 大key问题其实可以说是大value问题&#xff0c;就是某个key对应的value所占据的存储空间太大了&#xff0c;所以导致我们在操作这个key的时候花费的时间过长&#xff08;序列化\反序列化&#xff09;&#xff0c;从而降低了redi…

TDengine IDMP 产品基本概念

基本概念 元素 (Element) IDMP 通过树状层次结构来组织数据&#xff0c;树状结构里的每个节点被称之为元素 (Element)。元素是一个物理的或逻辑的实体。它可以是具体的物理设备&#xff08;比如一台汽车&#xff09;&#xff0c;物理设备的一个子系统&#xff08;比如一台汽车的…