JavaScript 垃圾回收机制深度解析:内存管理的艺术

在这里插入图片描述

🔥 个人主页:空白诗

在这里插入图片描述

文章目录

    • 🎭 引言
    • 一、JavaScript内存模型与生命周期的深度解析
      • 📌 基本数据类型与栈内存的精妙运作
      • 📌 复杂数据类型与堆内存的广袤世界
      • 📌 生命周期管理的智慧与实践策略
      • 📌 WeakMap 和 WeakSet 介绍
    • 二、垃圾回收机制(Garbage Collection, GC)的深度探索
      • 📌 引用计数法(Reference Counting)
      • 📌 标记-清除法(Mark-and-Sweep)
      • 📌 分代收集(Generational Collection)
    • 三、垃圾回收的智能进化:增量标记与并发/并行回收
      • 📌 增量标记(Incremental Marking):小步快跑,减少卡顿
      • 📌 并发/并行回收:多手多脚,效率倍增
      • 📌 总结
    • 四、内存泄漏防范手册:最佳实践指南
      • 1. 及时断开引用 —— 清理不再使用的对象
      • 2. 关注DOM引用 —— 解除事件监听与元素关联
      • 3. 定时器与回调管理 —— 控制异步资源
      • 4. 闭包中的变量 —— 精确管理作用域
      • 5. 模块与单例 —— 适度使用全局
    • 六、面试考点
      • 1. JavaScript中垃圾回收机制的基本原理是什么?
      • 2. 什么是引用计数法?它的缺点是什么?
      • 3. 标记清除算法是如何工作的?为什么它能解决循环引用问题?
      • 4. 什么是分代收集?它如何提高垃圾回收的效率?
      • 5. 如何在JavaScript中避免内存泄漏?
    • 七、总结

🎭 引言

在这个信息爆炸的时代,每一行代码都可能成为数字海洋中的璀璨星光,亦或是悄然累积的暗流。JavaScript,作为互联网世界的通用语言,它的每一次呼吸——数据的创建与消亡,都离不开垃圾回收机制的精密调控。在这篇文章中,我们将携手踏上一场深入JavaScript内存管理腹地的探险之旅,揭开垃圾回收机制那既神秘又强大的面纱,让每一次内存的分配与回收,都能成为推动应用高效运转的强劲动力。

在这里插入图片描述

一、JavaScript内存模型与生命周期的深度解析

📌 基本数据类型与栈内存的精妙运作

JavaScript内存管理的根基深植于栈内存堆内存的双重架构之中。栈内存以其快速访问速度和严谨的生命周期管理著称,它主要承载着程序中的轻量级成员——基本数据类型,覆盖了数字number、字符串string、布尔值boolean、特殊标识undefinednull。栈内存的另一项重要职责是记录函数调用时的临时住客——局部变量和执行上下文,确保一旦函数执行完毕或变量走出了自己的作用范围,它们占用的内存空间就能迅速被释放,从而维护内存使用的高效率和及时周转。

let age = 25; // 为age在栈内存中分配空间,并储存数字25
function greet() {
    let message = "Hello!"; // greet执行期间,在栈内存为message分配临时住所
}
greet(); // 函数演出结束,message占用的栈内存空间随即被回收

📌 复杂数据类型与堆内存的广袤世界

相较于栈内存的瞬时特性,堆内存如同一片广阔无垠的天地,为复杂数据类型如对象object、数组array以及函数(本质上作为对象存在)提供了一片生存的沃土。在这里,数据的家需要通过构造函数或字面量的方式显式建立。由于这些复杂结构可能被多个变量共享引用,甚至自身内部也包含引用关系,它们的生命周期管理就变得错综复杂,这也正是垃圾回收机制大展身手的舞台,负责甄别并清理那些已经孤立无援、不再被任何变量引用的内存区域。

let person = { name: "Alice" }; // 在堆内存中为person建造一座房子,并在栈内存保留一张前往该住址的地图

📌 生命周期管理的智慧与实践策略

深入理解栈内存与堆内存各自的生命周期管理机制,对于编写高效、无内存泄漏的JavaScript代码来说至关重要。

  • 栈内存的自动清理机制确保了瞬时数据的迅速释放,展现了自动化的高效;
  • 堆内存的动态性则要求开发者必须具备良好的内存管理意识,通过合理设计和适时解除对象引用,与垃圾回收机制相辅相成,共同抵御内存泄漏的威胁。

在实际编程实践中,以下几点是每位开发者应铭记于心的原则:

  • 及时释放不再使用的对象引用,避免无谓的内存占用;
  • 利用工具辅助,如浏览器的开发者工具进行内存分析,定期检查和定位内存泄漏;
  • 谨慎处理事件监听器和定时器,确保在不再需要时及时清理;
  • 了解并运用WeakMap和WeakSet,它们持有的弱引用可以减少内存泄漏的风险。

通过这些策略的实施,开发者不仅能提升应用的性能表现,还能确保资源的高效利用,让程序在内存的舞台上轻盈起舞,演绎出流畅无阻的用户体验。

📌 WeakMap 和 WeakSet 介绍

WeakMapWeakSet 是 ES6 中引入的两种特殊集合类型,它们与 MapSet 类似,但在内存管理上有所不同,主要体现在它们对所存储对象的引用是弱引用(weak reference)。这意味着,如果一个对象仅通过 WeakMapWeakSet 引用,当这个对象不再被其他地方使用时,垃圾回收机制可以回收该对象,即使它还存在于 WeakMapWeakSet 中。

WeakMap

WeakMap 是一个键值对集合,其键必须是对象类型,而值可以是任意类型。适合用于存储对象的私有数据或元数据,不会阻止垃圾回收机制回收对象。

let user = { name: "Alice" };

// 创建一个 WeakMap
let userMetadata = new WeakMap();

// 向 WeakMap 添加数据
userMetadata.set(user, { role: "Admin", joined: new Date() });

console.log(userMetadata.get(user)); // 输出: { role: "Admin", joined: Thu May 08 2024 11:55:35 GMT+0800 (China Standard Time) }

// 假设 user 对象不再被其他地方引用,垃圾回收机制可以回收 user 对象及其在 WeakMap 中的元数据
user = null;

WeakSet

WeakSet 是一个集合类型,只接受对象作为成员,并且对这些成员的引用是弱引用。当对象不在其他地方被引用时,即便它还在 WeakSet 中,也会被垃圾回收。

class Node {
  constructor(id) {
    this.id = id;
  }
}

let nodeA = new Node(1);
let nodeB = new Node(2);

// 创建一个 WeakSet
let processedNodes = new WeakSet();

// 向 WeakSet 添加对象
processedNodes.add(nodeA);
processedNodes.add(nodeB);

console.log(processedNodes.has(nodeA)); // 输出: true

// 假设 nodeA 不再被其他地方引用
nodeA = null;

// 垃圾回收机制可以回收 nodeA,同时它在 WeakSet 中的表示也会被移除
console.log(processedNodes.has(nodeA)); // 在垃圾回收之后,输出: false

这些示例展示了 WeakMapWeakSet 如何在不阻止垃圾回收的情况下存储对象的关联数据或跟踪对象集合。在需要管理对象生命周期和避免内存泄漏的场景中,它们是非常有用的工具。


二、垃圾回收机制(Garbage Collection, GC)的深度探索

📌 引用计数法(Reference Counting)

作为最直观的垃圾回收策略,引用计数机制赋予每个对象一个引用计数器,用来追踪该对象被引用的次数。每当创建新的引用时,计数器加1;当引用被释放(如变量被重新赋值或作用域结束)时,计数器减1。一旦对象的引用计数降至0,表明它不再被任何变量引用,随即被标记为可回收资源。

function referenceCountingExample() {
    let obj1 = new Object(); // obj1的引用计数初始化为1
    let obj2 = obj1;       // obj1的引用计数因被obj2引用而增加至2
    obj1 = null;          // obj1的引用被解除,但因obj2的引用,计数仍为1
    obj2 = null;          // 最后一个引用被解除,对象的引用计数为0,准备回收
}

注意事项⚠️
尽管简单直接,引用计数机制在处理循环引用场景时显得无能为力,容易导致内存泄漏问题,即两个或多个对象相互引用,即使它们已不再被外部使用,计数也不会降为0。

📌 标记-清除法(Mark-and-Sweep)

为克服引用计数的局限性,标记-清除算法应运而生。此算法通过两阶段实现内存的回收:

  1. 标记阶段:从根对象(如全局对象)出发,遍历所有可达的对象,将其标记为“活着”或“可达”状态。
  2. 清除阶段:遍历堆内存,未被标记的所有对象被视为垃圾,回收它们占用的内存空间。
function markAndSweepExample() {
    let objA = {}; 
    let objB = {}; 
    objA.ref = objB; // 形成循环引用
    objB.ref = objA;
    objA = null; 
    objB = null;
    // 标记-清除机制能够识别并处理此类循环引用,确保无用对象被回收
}

📌 分代收集(Generational Collection)

鉴于大多数对象具有短暂生命周期的特性,现代JavaScript引擎采用了分代收集策略,将堆内存划分为年轻代老年代。新创建的对象首先进入年轻代,经历一轮或几轮垃圾回收后仍存活的对象会被晋升到老年代。这一策略的优势在于,针对年轻代频繁执行低成本的回收,而老年代则执行较不频繁但成本较高的回收,从而显著提升了GC的整体效率。

  • 年轻代:频繁进行快速回收,适用于短期生存的对象。
  • 老年代:较少执行回收,但每次回收更彻底,适合长期存活的对象。

通过以上机制的综合运用,JavaScript的垃圾回收系统能够在保持内存使用效率的同时,最小化因垃圾回收引起的程序暂停,确保应用运行的流畅与高效。


三、垃圾回收的智能进化:增量标记与并发/并行回收

📌 增量标记(Incremental Marking):小步快跑,减少卡顿

想象一下,垃圾回收就像家里大扫除,如果一次性打扫所有房间,可能会占用很长时间,期间家人就无法正常使用客厅了。增量标记就是把大扫除分成很多小任务,比如每次只打扫一个角落,打扫完快速休息一下,让家人可以继续在其他地方活动,然后再打扫下一个角落。这样,虽然打扫时间可能总长一些,但家里人几乎感觉不到被打扰。

JavaScript的世界里,增量标记就是将原本可能引起长时间卡顿的垃圾回收过程,拆解成一系列小步骤,在代码执行的间隙逐一执行。这样,即便在执行垃圾回收,应用也不会突然“冻结”,用户体验更加流畅。

示例代码(概念展示)
虽然我们不能直接控制垃圾回收的具体执行,但可以通过模拟理解其背后的逻辑:

// 模拟应用逻辑:执行一些操作,期间垃圾回收可能在后台进行增量标记
function simulateAppLogic() {
    for(let i = 0; i < 10; i++) {
        // 模拟执行一些操作,如处理数据、渲染页面等
        console.log(`处理第 ${i} 步数据...`);
        
        // 模拟增量标记的“间隙”,这里实际上由引擎自动管理
        // 但我们可以想象在此期间,GC可能在悄悄工作
    }
    console.log("所有操作完成!");
}

📌 并发/并行回收:多手多脚,效率倍增

并发回收就像是家里有两个人同时打扫,一个人负责扫地,另一个人负责擦桌子,两人同时工作,家务完成得更快。而并行回收则是家里有多个房间,每个房间都有人在打扫,大家同时动手,整个房子的清洁工作迅速完成。

JavaScript引擎中,并发回收意味着垃圾回收可以在与主线程不同的另一个线程中同时进行,两者互不干扰。并行回收则是在有多核CPU的情况下,垃圾回收的不同部分可以同时在不同的CPU核心上执行,实现真正的并行处理,极大提升了回收速度。

示例代码(概念展示)
继续使用之前的模拟,理解并发或并行的思想:

// 假想的并发/并行回收概念展示
function imagineConcurrentParallelGC() {
    console.log("开始并发/并行GC...");
    // 这里实际由引擎控制,模拟时无法直接实现,但理解为GC在后台高效进行
    // 并发情况下,GC与应用代码交替进行;并行情况下,多核CPU上同时工作
    console.log("GC任务在后台悄悄进行,不影响应用逻辑...");
}

📌 总结

通过增量标记与并发/并行回收技术,现代JavaScript引擎能在保持应用运行流畅的同时,高效管理内存。开发者虽不能直接控制这些机制,但理解其原理对于写出更高效、响应更快的代码至关重要。就如同生活中合理的安排家务,让家里始终保持整洁,而家人几乎感受不到打扫的存在,JavaScript的垃圾回收机制也在幕后默默地为我们创造了一个流畅的数字家园。


四、内存泄漏防范手册:最佳实践指南

1. 及时断开引用 —— 清理不再使用的对象

当对象不再服务于任何用途时,主动将其引用设为null,可以帮助垃圾回收机制更快识别并释放该对象占用的内存。

function processData(data) {
    let heavyObject = createHeavyObject(); // 创建一个大对象
    // ...使用heavyObject...
    heavyObject = null; // 不再使用时,断开引用
}

2. 关注DOM引用 —— 解除事件监听与元素关联

动态添加到DOM上的元素及其事件监听器,如果不恰当处理,可能形成内存泄漏。移除元素时,务必解除相关的事件监听。

let button = document.createElement('button');
button.textContent = 'Click me';
document.body.appendChild(button);

button.addEventListener('click', handleClick);

// 当不再需要时,正确移除元素和监听器
button.removeEventListener('click', handleClick);
document.body.removeChild(button);

3. 定时器与回调管理 —— 控制异步资源

使用setTimeoutsetInterval时,确保在不再需要时清除定时器,避免无尽的等待和内存占用。

let intervalId = setInterval(updateUI, 1000);

function cleanup() {
    clearInterval(intervalId); // 清除不再需要的定时器
}
// 适时调用cleanup函数,比如在组件卸载时

4. 闭包中的变量 —— 精确管理作用域

闭包能维持变量的生命期,但也可能导致意外的内存泄漏。确保闭包内只保留必要变量,并在可能时释放不再需要的变量。

function createClosure() {
    let tempArr = []; // 可能的内存泄漏源
    return function() {
        // 如果可以,清理泄露变量
        // ...
    };
}

5. 模块与单例 —— 适度使用全局

滥用全局变量会增加内存泄漏的风险,特别是在大型应用中。采用模块模式或ES6模块,限制作用域,减少全局污染。

// 使用ES6模块避免全局变量
export function someFunction() {
    // ...
}
// 或者使用立即执行函数表达式包裹模块
(function() {
    var privateVar; // 私有变量
    window.myLibrary = { // 公开接口
        publicMethod: function() {
            // 使用privateVar...
        }
    };
})();
})();

遵循这些最佳实践,能显著降低内存泄漏的风险,使你的JavaScript应用更加健壮、响应迅速。内存管理是每个开发者都应持续关注并优化的领域,它直接影响到应用的性能和用户体验。


六、面试考点

在面试中,有关JavaScript垃圾回收机制的问题经常围绕以下几个核心概念和实践展开,以下是一些常考问题及其简要回答示例:

1. JavaScript中垃圾回收机制的基本原理是什么?

回答JavaScript的垃圾回收机制主要是为了自动管理内存,防止内存泄漏。它基于两种主要算法:引用计数标记-清除。引用计数通过跟踪对象的引用数量来决定是否回收,而标记-清除则通过遍历所有可达对象并标记,未被标记的对象视为垃圾进行回收。现代JavaScript引擎还采用了分代收集和增量标记、并发回收等策略以提高效率。

2. 什么是引用计数法?它的缺点是什么?

回答引用计数法是通过跟踪对象被引用的次数来决定是否回收。当一个对象的引用计数变为0时,该对象被视为垃圾并回收。它的缺点在于无法处理循环引用问题,即两个或多个对象相互引用,即使不再被使用,引用计数也不降为0,导致内存泄漏。

3. 标记清除算法是如何工作的?为什么它能解决循环引用问题?

回答标记清除算法分为两步:首先从根对象开始,遍历所有可达对象并标记为活动对象,然后遍历堆内存,未被标记的对象即为垃圾并回收。它解决了循环引用问题,因为循环引用的对象虽然互相引用,但如果它们不再可从根对象到达,依然会被标记为不可达并回收。

4. 什么是分代收集?它如何提高垃圾回收的效率?

回答:分代收集将堆内存分为年轻代和老年代。新创建的对象通常分配在年轻代,经历一次GC后仍然存活的对象会晋升到老年代。年轻代的垃圾回收频繁且速度快,因为它假设多数对象寿命短;老年代回收少但更彻底,针对长寿对象。这种方式通过减少对老对象的频繁扫描,显著提高了回收效率。

5. 如何在JavaScript中避免内存泄漏?

回答

  • 及时解除不再使用的对象引用:例如,设置为null
  • 管理DOM引用:移除DOM节点时,记得移除事件监听器。
  • 定时器和回调清理:确保取消不需要的setTimeoutsetInterval
  • 闭包谨慎使用:避免不必要的变量长期驻留在闭包中。
  • 避免全局变量滥用:减少全局变量,使用模块或局部变量。

这些问题涵盖了垃圾回收机制的基本概念、优缺点、具体算法的运作方式以及开发者如何在实践中避免内存泄漏的关键点,是面试中考察JavaScript内存管理理解的常见维度。


七、总结

本文深度剖析了JavaScript垃圾回收机制的核心原理与实战策略,为开发者铺就了一条通向高效内存管理的道路。要点概括如下:

  1. 内存的双域世界:阐明了JavaScript中的内存分为栈内存和堆内存,前者存储基本类型变量,后者负责复杂数据结构,奠定了理解垃圾回收的基础框架。

  2. 回收机制的哲学:介绍了两种主要的垃圾回收策略——引用计数与标记-清除。引用计数直接追踪对象的引用次数,简单直观;而标记-清除则通过根对象出发遍历,区分可达与不可达对象,实现内存的回收,解决了循环引用的问题。

  3. 分代收集的智慧:阐述了现代JavaScript引擎采用的分代收集策略,将堆内存划分为新生代和老生代,针对不同对象的生命周期采取不同的回收策略,提升了回收效率。

  4. 优化与挑战:探讨了增量标记、并发回收等高级技术,这些机制进一步优化了垃圾回收过程,减少了内存管理对应用性能的影响,确保了应用的流畅运行。

  5. 开发者实践:强调了实践中避免内存泄漏的重要性,提供了识别与解决内存管理问题的方法,鼓励开发者采用工具辅助检测,编写更加健壮的代码。

  6. 结语展望:以鼓舞人心的话语作结,激励开发者持续探索JavaScript的深度与广度,成为精通内存管理的大师,不断追求代码的卓越与应用的极致性能。

总之,掌握JavaScript垃圾回收机制不仅是技术的提升,更是对应用品质的承诺。在编程的无尽探索中,愿你凭借这份知识,开辟出更高效、更稳定的技术天地。


在这里插入图片描述

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

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

相关文章

uniapp开发小程序使用vue的v-html解析富文本图片过大过宽显示超过屏幕解决办法

如果没有设置的话&#xff0c;就会导致图片溢出&#xff0c;过宽显示或者错位显示&#xff0c;显示效果非常的丑陋&#xff1a; 修改后显示的效果&#xff1a; 网上比较low的解决办法&#xff1a;网上各种解决方法核心思想就是在数据层把数据模板上的img数据加上style样式&…

嵌入式Linux学习第四天启动方式学习

嵌入式Linux学习第四天 今天学习I.MX6U 启动方式详解。I.MX6U有多种启动方式&#xff0c;可以从 SD/EMMC、NAND Flash、QSPI Flash等启动。 启动方式选择 BOOT 的处理过程是发生在 I.MX6U 芯片上电以后&#xff0c;芯片会根据 BOOT_MODE[1:0]的设置来选择 BOOT 方式。BOOT_M…

windows11获取笔记本电脑电池健康报告

笔记本电脑的电池关系到我们外出时使用的安全&#xff0c;如果电池健康有问题需要及时更换&#xff0c;windows系统提供了检查电池健康度的方法。 1、打开命令行 1&#xff09;键入 winR 2&#xff09;键入 cmd 打开命令行。 2、在命令行运行如下指令&#xff0c;生成电池健…

Maven+Junit5 + Allure +Jenkins 搭建 UI 自动化测试实战

文章目录 效果展示Junit 5Junit 5 介绍Junit 5 与 Junit 4 对比PageFactory 模式编写自动化代码公共方法提取测试用例参数化Jenkins 搭建及配置参数化执行生成 Allure 报告Maven 常用命令介绍POM 文件效果展示 本 chat 介绍 UI 自动化测试框架的搭建: 运用 page factory 模式…

TCP三次握手四次挥手 UDP

TCP是面向链接的协议&#xff0c;而UDP是无连接的协议 TCP的三次握手 三次传输过程是纯粹的不涉及数据&#xff0c;三次握手的几个数据包中不包含数据内容。它的应用层&#xff0c;数据部分是空的&#xff0c;只是TCP实现会话建立&#xff0c;点到点的连接 TCP的四次挥手 第四…

介绍 ffmpeg.dll 文件以及ffmpeg.dll丢失怎么办的五种修复方法

ffmpeg.dll 是一个动态链接库文件&#xff0c;属于 FFmpeg运行库。它在计算机上扮演着非常重要的角色&#xff0c;因为它提供了许多应用程序和操作系统所需的功能和组件。当 ffmpeg.dll 文件丢失或损坏时&#xff0c;可能会导致程序无法正常运行&#xff0c;甚至系统崩溃。下面…

基于opencv的车辆统计

车辆统计&#xff09; 一、项目背景二、整体流程三、常用滤波器的特点四、背景减除五、形态学开运算闭运算 六、项目完整代码七、参考资料 一、项目背景 检测并识别视频中来往车辆的数量 最终效果图&#xff1a; 二、整体流程 加载视频图像预处理&#xff08;去噪、背景减除…

航空电子FC节点卡, FC交换机,主要采用LC或MPO光纤接口形式

FC节点卡主要采用LC或MPO光纤接口形式&#xff0c;可以作为4/2个独立端口使用&#xff0c;也可以作为2对/1对冗余端口使用&#xff0c;支持1.0625Gbps、2.125Gbps、4.25Gbps、8.5Gbps通信速率。节点卡完全遵循FC-LS&#xff0c;FC-FS&#xff0c;FC-AE-ASM、FC-AE-1553B等FC光纤…

springboot+vue+mybatis家教管理系统的设计与实现+PPT+论文+讲解+售后

家教管理系统是为了方便用户能够在网站上查看教师信息进行家教预约等&#xff0c;于是开发了基于springboot框架设计与实现了一款简洁、轻便的家教管理系统。本系统解决了家教管理事务中的主要问题&#xff0c;包括以下多个功能模块&#xff1a;公告信息、留言信息、预约家教老…

Isaac Sim 4 键盘控制小车前进方向(学习笔记5.8.2)

写的乱糟糟&#xff0c;主要是这两周忘了记录了...吭哧吭哧往下搞&#xff0c;突然想起来要留档&#xff0c;先大致写一个&#xff0c;后面再往里添加和修改吧&#xff0c;再不写就全忘了 有一个一直没解决的问题&#xff1a; 在保存文件时出现问题&#xff1a;isaac sim mism…

【OceanBase诊断调优】—— 磁盘性能问题导致卡合并和磁盘写入拒绝排查

适用版本 OceanBase 数据库 V3.x、V4.x 版本。 问题现象 OceanBase 集群合并一直未完成&#xff0c;同时 tsar 和 iostat 显示从凌晨 2:30 开始磁盘使用率一直是 100%。怀疑合并导致 IO 上升&#xff0c;IO 可能存在问题&#xff0c;observer.log 的确有大量报错 disk is hu…

概述篇——计算机网络学习笔记(基于谢希仁教材与PPT)

教材用的是谢希仁的教材&#xff0c;图片来源于谢希仁老师配套的PPT 计算机网络课程PPT&#xff08;通过这个链接&#xff0c;你可以找到课程配套的ppt&#xff09; 计算机网络的定义及其特点 定义 网络 过去大众熟知的三种网络分别是提供电话、电报及传真等服务的电信网络&am…

Linux主机排查工具-GScan

0x01 简介 本程序旨在为安全应急响应人员对Linux主机排查时提供便利&#xff0c;实现主机侧Checklist的自动全面化检测&#xff0c;根据检测结果自动数据聚合&#xff0c;进行黑客攻击路径溯源。 0x02 项目地址 https://github.com/grayddq/GScan 0x03 CheckList检测项 自…

每日一题——力扣面试题 17.04. 消失的数字

题目链接&#xff1a;https://leetcode.cn/problems/missing-number-lcci/description/ 菜鸡做法&#xff1a; #include <stdlib.h> // 包含标准库头文件&#xff0c;用于内存分配等功能// 函数定义&#xff1a;寻找缺失的数字 int missingNumber(int* nums, int numsSi…

从离线到实时:无锡锡商银行基于 Apache Doris 的数据仓库演进实践

作者&#xff1a;武基鹏&#xff0c;无锡锡商银行 大数据技术经理 编辑整理&#xff1a;SelectDB 技术团队 导读&#xff1a;为实现数据资产的价值转化以及全面数字化、智能化的风险管理&#xff0c;无锡锡商银行大数据平台经历从 Hive 离线数据仓库到 Apache Doris 实时数据仓…

Hive SQL-DQL-Select查询语句用法详解

HQL Select用法详解 1.基础语法 &#xff08;1&#xff09;select_exp &#xff08;2&#xff09;ALL、DISTINCT &#xff08;3&#xff09;WHERE &#xff08;4&#xff09;分区查询、分区裁剪 &#xff08;5&#xff09;GROUP BY &#xff08;6&#xff09;HAVING &#xff0…

hadoop学习---基于Hive的教育平台数据仓库分析案例(三)

衔接第一部分&#xff0c;第一部分请点击&#xff1a;基于Hive的教育平台数据仓库分析案例&#xff08;一) 衔接第二部分&#xff0c;第二部分请点击&#xff1a;基于Hive的教育平台数据仓库分析案例&#xff08;二) 学生出勤模块&#xff08;全量分析&#xff09;&#xff1a…

Densenet+SE

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊# 前言 前言 这周开始学习关于经典模型的改进如加注意力机制&#xff0c;这周学习Densenet加通道注意力即SE注意力机制。 ##SE注意力机制简介 SE&#xff08;…

自定义shell

1、首先我们的程序要打印出命令行 命令行》用户名【主机名】当前路劲$:命令字符串 用户名、主机名、当前路径可以通过系统调用函数getenv()得到&#xff1a; 2、获取命令字符串 把输入的命令字符串放到一个指针数组中 但是我们发现用scanf函数输入的话&#xff0c;遇到空…

【数据结构】-- 链表专题

链表的分类 前面我们实现了单链表&#xff0c;单链表只是链表的一种。可以根据以下几个标准来判断链表的类型&#xff1a; 1.单向或者双向 如图所示&#xff0c;单向链表中一个节点的指针域只储存了下一个节点的指针&#xff0c;能通过前一个节点访问后一个节点&#xff0c;无…