一文彻底了解浏览器垃圾回收机制

前言

  • 常网IT源码上线啦!
  • 本篇录入吊打面试官专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
  • 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
  • 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。

沟通三要素

1,语速快或慢;2,留点空档给人发言;3,观察变化,适当调整

现场看,还是有点震撼的,致敬。

一、前言

内存管理是很重要的,非常影响性能。

垃圾回收(Garbage Collection,GC)作为自动内存管理机制,通过智能识别和释放不再使用的内存对象,解决了手动内存管理中的常见问题。在浏览器环境中,垃圾回收机制尤为重要,原因有三:

  1. 安全防护:JavaScript作为动态类型语言,开发者在编码过程中很难精确控制每个对象的生命周期。自动垃圾回收机制有效防止了内存泄漏和野指针等问题。

  2. 性能优化:单页面应用的内存占用量可达数百MB。高效的垃圾回收能维持浏览器稳定运行,防止页面崩溃。话说最近接手一个react项目,本地运行容易就崩。

  3. 开发效率:开发者无需手动分配和释放内存,可以更专注于业务逻辑实现。

直入正文。

二、引用标记、标记清除

引用计数法:直观但局限

  • 每个对象创建时初始化引用计数器(通常为1)

  • 当对象被引用时,计数器增加

  • 引用关系解除时,计数器减少

  • 当计数器归零时,对象被立即回收

缺陷:循环引用问题。

java 复制代码
function createCyclicReference() {
    const objA = {};
    const objB = {};
    
    objA.ref = objB;  // objB计数器=1
    objB.ref = objA;  // objA计数器=1
    
    // 函数结束后,计数器仍为1
    // 内存永远无法释放
}
  • 循环引用问题:对象相互引用导致计数器永不为零

  • 计数器开销:每个对象需要额外存储空间记录引用数

  • 实时性陷阱:频繁更新计数器导致性能损耗

标记清除法

三阶段工作流程

标记阶段(Marking)

  • 从根对象(全局对象、当前函数调用栈等)出发

  • 递归遍历所有可达对象并标记为活动状态

  • 采用深度优先搜索(DFS)策略

清除阶段(Sweeping)

  • 遍历堆内存中的所有对象

  • 回收未被标记的对象所占内存

  • 将回收的内存块加入空闲列表

整理阶段(Compacting)

  • 移动存活对象使其在内存中连续排列

  • 消除内存碎片,形成大块连续空间

  • 更新所有指向移动对象的引用指针

算法优势

  • 完美解决循环引用问题

  • 内存利用率显著提高

  • 全堆扫描确保无遗漏回收

三、那浏览器的V8用的啥

分代假说

V8的内存管理基于两个关键观察:

新生代(Young Generation)

  • 绝大多数对象生命周期极短

  • 约98%的对象在第一次GC时被回收

  • 分配速度快但回收频繁

老生代(Old Generation)

  • 存活超过一次GC的对象晋升至此

  • 对象存活时间长,回收频率低

  • 内存占用大,回收成本高

新生代回收:Scavenge算法

回收过程

  1. 遍历From Space中的活动对象(当前对象分配区)

  2. 将活动对象复制到To Space(上次GC存活对象存放区)

  3. 复制过程中完成内存整理

  4. 交换From Space和To Space角色

  5. 满足晋升条件的对象移至老生代

晋升条件

  • 对象在新生代存活超过两次GC

  • To Space使用率超过25%

  • 对象大小超过256KB(直接晋升)

老生代回收:三色标记法

标记优化策略

  • 白色:未访问对象(待回收)

  • 灰色:已访问但子对象未完全检查

  • 黑色:已访问且子对象全部检查完成

并发回收:无停顿体验

并行标记

  • 主线程与回收线程同时工作

  • 多核CPU并行标记不同内存区域

  • 回收速度提升3-5倍

并发回收

  • 回收线程独立于主线程运行

  • 完全消除用户可感知的停顿

  • 需要解决内存访问冲突问题

并行回收:牺牲内存空间换取时间效率

四、内存泄漏

  1. 意外全局变量
java 复制代码
function leakMemory() {
    leakedData = new Array(1000000); // 未声明变量
}
  1. 遗忘的定时器
java 复制代码
const timer = setInterval(() => {
    const data = loadData(); // data被持续引用
    updateUI(data);
}, 1000);

// 忘记clearInterval(timer)
  1. DOM引用残留
java 复制代码
const elements = {
    button: document.getElementById('myBtn'),
    container: document.getElementById('container')
};

// 移除DOM但未删除引用
document.body.removeChild(elements.container);

诊断工具链

Performance Monitor:

  • 实时监控JS堆大小

  • 观察GC频率及时长

Memory面板:

  • Heap Snapshot:堆内存快照

  • Allocation Timeline:内存分配时间线

  • Allocation Sampling:分配采样分析

内存分析指标

  • JS Heap Size:JavaScript对象占用内存

  • DOM Nodes:DOM节点数量

  • Event Listeners:事件监听器数量

  • GPU Memory:显存使用量

五、Good Code

  1. 对象池技术
java 复制代码
class ObjectPool {
    constructor(createFn) {
        this.pool = [];
        this.createFn = createFn;
    }

    acquire() {
        return this.pool.pop() || this.createFn();
    }

    release(obj) {
        this.pool.push(obj);
    }
}

// 使用示例
const vectorPool = new ObjectPool(() => ({x:0, y:0}));
const v1 = vectorPool.acquire();
// 使用后归还
vectorPool.release(v1);
  1. 避免内存抖动

对整个对象进行重新赋值 ~ 谨慎

java 复制代码
// 反例:频繁创建临时对象
function updatePositions() {
    objects.forEach(obj => {
        obj.position = {x: Math.random(), y: Math.random()};
    });
}

// 正例:复用内存
function updatePositions() {
    objects.forEach(obj => {
        obj.position.x = Math.random();
        obj.position.y = Math.random();
    });
}

至此撒花~

后记

知道之后,有了更深刻的认识。

明白了时间的价值:用户可感知的停顿与流畅体验,还有算法的精妙。

正如V8引擎首席架构师所说:"垃圾回收不是科学,而是艺术------在约束中创造自由,在混沌中建立秩序"。

我们在实际项目中或多或少遇到一些奇奇怪怪的问题。

自己也会对一些写法的思考,为什么不行🤔,又为什么行了?

最后,祝君能拿下满意的offer。

我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车

👍 如果对您有帮助,您的点赞是我前进的润滑剂。

以往推荐

如何在不发版时,实现小程序的 AB 测试?

大白话学习性能优化requestAnimationFrame

为什么一定要有微任务,直接一个宏任务不行吗

深入Vue3/React Hooks

聊聊小程序的双线程架构

Vue性能优化:从加载提速到运行时优化

vue2和Vue3和React的diff算法展开说说:从原理到优化策略

前端哪有什么设计模式

小小导出,我大前端足矣!

前端仔,快把dist部署到Nginx上

多图详解,一次性啃懂原型链(上万字)

Vue-Cli3搭建组件库

Vue实现动态路由(和面试官吹项目亮点)

VuePress搭建项目组件文档

原文链接

juejin.cn/post/754056...

相关推荐
页面魔术2 分钟前
无虚拟dom怎么又流行起来了?
前端·javascript·vue.js
胡gh2 分钟前
如何聊懒加载,只说个懒可不行
前端·react.js·面试
Double__King6 分钟前
巧用 CSS 伪元素,让背景图自适应保持比例
前端
Mapmost7 分钟前
【BIM+GIS】BIM数据格式解析&与数字孪生适配的关键挑战
前端·vue.js·three.js
一涯8 分钟前
写一个Chrome插件
前端·chrome
鹧鸪yy15 分钟前
认识Node.js及其与 Nginx 前端项目区别
前端·nginx·node.js
跟橙姐学代码16 分钟前
学Python必须迈过的一道坎:类和对象到底是什么鬼?
前端·python
汪子熙18 分钟前
浏览器里出现 .angular/cache/19.2.6/abap_test/vite/deps 路径究竟说明了什么
前端·javascript·面试
Benzenene!19 分钟前
让Chrome信任自签名证书
前端·chrome