前端学习20 垃圾回收机制

在前端中,内存管理是一个非常重要的领域。浏览器的垃圾回收机制(Garbage Collection ,GC)是现在浏览器中的核心的部分之一,它可以自动管理内存的分配与回收,帮助开发者避免手动管理内存,从而减少内存泄露和性能问题的风险。

1.垃圾回收机制

垃圾回收是一种自动内存管理机制,用于识别和释放不再使用的内存,垃圾回收的核心目标是释放不在使用的内存空间。在浏览器中,当页面中的某些对象不在被需要时,垃圾回收机制会自动将他们从内存中移除,确保不会发生内存泄露。

JavaScript是一种内存自动管理的语言,内存管理的核心就是垃圾回收。

它主要通过两种方式来检测和释放内存:

  • 引用计数(Reference Couning):追踪每个对象的引用次数,当一个对象的引用次数为零,就认为该对象不再使用,可以释放它占用的内存。
  • 标记清除(Mark-and-Sweep):通过标记所有可达的对象,然后清除所有玮柏哦记得对象来回收内存。

引用计数曾经是早期垃圾回收机制中的一个重要方法,但由于其无法解决循环引用、性能开销较大以及不能有效解决内存碎片问题,现代浏览器和大多数编程语言已经转向使用更高效的垃圾回收策略,如 标记-清除算法分代回收增量回收 和 并行回收 等。

2.垃圾回收机制的工作原理

2.1 引用计数

引用计数(Reference Counting)是一种较为基础的垃圾回收算法,它通过追踪对象的引用次数来判断对象是否可以被回收。每个对象都维护一个计数器,表示它被多少个其他对象引用。当一个对象的引用计数降为零时,表示没有任何引用指向该对象,可以安全地回收该对象的内存。

工作原理:

  • 初始化引用次数:每当一个对象被创建并将其引用赋值给一个变量时,该对象的引用计数为1。
  • 对象引用增加与减少当一个对象被引用时(如赋值给其他变量或作为函数参数传递),它的引用计数增加;当一个引用被销毁时(如局部变量超出作用域或赋值为null),该对象的引用计数减少。
  • 回收垃圾对象:当一个对象的引用计数降到零时,意味着没有任何引用指向该对象,垃圾回收器会回收该对象占用的内存。
let obj1 = { name: "Object 1" };  // 引用计数为 1
let obj2 = obj1;                   // 引用计数为 2,因为 obj2 引用了 obj1
let obj3 = { ref: obj1 };          // 引用计数为 3,因为 obj3 引用了 obj1

obj2 = null;  // 引用计数为 2,obj1 的引用计数减少 1
obj3 = null;  // 引用计数为 1,obj1 的引用计数再次减少 1

obj1 = null;// 此时,obj1 的引用计数变为 0,垃圾回收器可以回收 obj1

在上述代码中,obj1 的初始引用计数为 1,后来通过 obj2 和 obj3 增加了引用计数。当 obj2 和 obj3,obj1 被设置为 null 时,obj1 的引用计数逐渐减为 0,垃圾回收器可以回收 obj1 占用的内存。

但是引用计数会存在一定问题:

  • 循环引用问题

引用计数的一个显著缺点是无法解决循环引用问题。例如,如果两个对象互相引用对方,它们的引用计数始终大于 0,即使它们不再被其他对象引用,垃圾回收器也无法将它们回收,从而导致内存泄漏。

2.2 标记清除

标记清除是现代垃圾回收器中常用的内存回收算法,广泛引用与JavaScript、Java、Python等编程与原的垃圾回收机制。它的核心思想是通过标记存活对象,并清除未标记的对象来实现内存的回收。

标记-清除算法的基本流程可以分为两个阶段:标记阶段(Mark Phase)和清除阶段(Sweep Phase)。

  • 标记阶段

在这个阶段,垃圾回收器会从根对象(Root Object)开始遍历,标记所有可以访问到的对象为“活跃的”。

根对象包括全局对象当前执行栈上的局部变量,活动函数等。

遍历所有可达的对象,并将这些对象标记为“活动”状态,意味着它们仍然被程序所引用。

  • 清除阶段

在标记阶段完成后,垃圾回收器会见检查堆中的所有对象。

所有没有被标记为活动的对象,(即不再被任何其他对象引用的对象)被认为是垃圾,可以回收并释放内存。

这是,垃圾回收器会删除这些不在需要的对象,释放他们占用的内存空间。

function createObjects() {
    let obj1 = { name: 'Object 1' };
    let obj2 = { name: 'Object 2' };
    let obj3 = { name: 'Object 3' };
    
    obj1.ref = obj2;  // obj1 引用 obj2
    obj2.ref = obj3;  // obj2 引用 obj3
    
    // 假设程序中不再使用 obj1 和 obj3
    obj1 = null;  // 断开 obj1 和 obj2 的引用
    obj2 = null;  // 断开 obj2 和 obj3 的引用
}

createObjects();

在上述代码中,obj1、obj2 和 obj3 都是对象,它们之间通过引用相互连接。在 createObjects 函数执行完后,obj1 和 obj2 都被设置为 null,它们之间的引用被断开。

2.3 分代回收

分代回收基于一个观察:大多数对象的生命周期很短,只有少数对象会存活较长时间。因此,垃圾回收器将内存分为不同的代(Generation),并对不同代采用不同的回收策略。

  • 新生代(Young Generation):存放新创建的对象。新生代的垃圾回收频率较高,采用复制算法(Copying Algorithm)进行回收。
  • 老生代(Old Generation):存放存活时间较长的对象。老生代的垃圾回收频率较低,采用标记-清除或标记-整理(Mark-and-Compact)算法进行回收。

生成垃圾回收算法基于这样一个假设:大部分对象会很快变得不可达,因此,年轻代的对象会频繁进行垃圾回收。只有生命周期较长的对象才会进入老年代,老年代的回收相对较少,避免频繁回收带来的性能损耗。

全部评论

相关推荐

关于客户端行业:入行需谨慎在当今这个科技飞速发展的时代,新兴行业如璀璨繁星般不断涌现,吸引着无数怀揣梦想与热情的年轻人投身其中。客户端领域,曾几何时也是众人瞩目的焦点,然而如今,我却想真诚地劝诫各位,入行客户端需慎重考虑。曾经,客户端行业宛如一片充满宝藏的新大陆,吸引着大批开拓者。那时候,市场需求旺盛,似乎只要有一款稍有特色的客户端产品推出,就能收获大量用户,获得可观的收益。各大公司纷纷布局,投入巨额资金进行研发和推广,行业内一片热火朝天的景象。开发人员备受尊崇,薪资待遇优厚,职业前景看似一片光明。但如今,客户端行业的辉煌已悄然褪色,现实的残酷正无情地摆在眼前。首先,市场竞争达到了白热化的程度。随着时间的推移,各类客户端应用层出不穷,几乎涵盖了生活的方方面面,市场已然趋近饱和。新的客户端想要在这片拥挤的红海之中崭露头角,难度堪比登天。海量的同类产品相互厮杀,用户的选择众多,导致新客户端获取用户的成本急剧攀升。为了吸引哪怕一小部分用户,企业往往需要投入巨额的营销费用,可最终的效果却常常不尽人意。许多创业团队满怀希望地进入这个领域,却在激烈的竞争中折戟沉沙,血本无归。其次,技术更新换代的速度令人目不暇接。客户端行业是一个典型的技术驱动型领域,新技术、新框架不断涌现。今天流行的技术,或许明天就会被淘汰。这就要求从业者必须时刻保持学习的状态,不断更新自己的知识体系,以跟上行业的发展步伐。对于初入行业的新人来说,不仅要掌握扎实的基础知识,还要花费大量的时间和精力去学习最新的技术,压力之大可想而知。而且,即便你努力跟上了技术的节奏,也不能保证你的技能就能一直适应市场的需求。一旦技术方向发生转变,之前的努力可能就会付诸东流,面临重新学习的困境。再者,客户端行业的盈利模式日益复杂且不稳定。过去,广告投放和付费会员是常见的盈利方式,但如今,随着用户对广告的抵触情绪越来越高,广告效果大打折扣,广告收入也随之减少。而付费会员模式,在竞争激烈的市场环境下,用户对于付费的意愿普遍较低,想要培养用户的付费习惯并非易事。此外,政策法规的不断变化也给行业的盈利带来了诸多不确定性。一些原本可行的盈利手段,可能因为政策的调整而被迫终止,企业不得不重新寻找盈利途径,这无疑增加了运营的风险。另外,客户端行业的工作强度极大,对从业者的身心健康造成了严重的挑战。为了赶项目进度、修复漏洞、应对紧急情况,加班加点成为了家常便饭。长期处于这种高强度的工作状态下,身体很容易出现各种问题,精神压力也会与日俱增。许多从业者在年纪轻轻时就患上了各种职业病,生活质量严重下降。而且,由于工作占据了大量的时间,个人的社交生活和家庭关系也往往受到影响,导致身心疲惫。所以,综合以上种种因素,客户端行业如今已不再是那个充满美好憧憬的理想之地。如果你还在考虑是否要踏入这个行业,希望你能充分了解其中的艰辛与风险,慎重做出决定。人生的选择至关重要,有时候,避开看似诱人实则布满荆棘的道路,也是一种智慧。#客户端#
投递新大陆科技集团等公司6个岗位
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客企业服务