第 8 卷
- 回收效率极致优化:新生代快速回收,老生代低频回收,平衡性能和内存占用;
- 大幅减少STW时间,提升程序运行流畅度;
- 适配绝大多数程序的内存使用规律(80%的对象存活时间短)。
缺点
- 实现复杂,垃圾回收器逻辑量大;
- 新生代复制算法有内存空间浪费(需预留空闲区域)。
应用语言
Java(HotSpot)、JavaScript(V8)、Go、C#(.NET)。
现代编程语言GC的核心特性
- 自动性:无需开发者手动干预,全流程自动完成;
- 透明性:开发者无需感知GC的执行过程,专注业务开发;
- 可控性:支持手动触发GC(如JS的 global.gc()、Java的 System.gc()),配置GC参数;
- 高效性:分代回收、并发回收、增量回收等优化,降低性能损耗;
- 安全性:避免内存越界、野指针、重复释放等问题。
主流编程语言的GC实现
- JavaScript:V8引擎,分代回收(新生代Copying,老生代Mark-Sweep/Mark-Compact);
- Java:HotSpot引擎,分代回收(新生代ParNew,老生代CMS/G1/ZGC);
- Go:三色标记法+写屏障,并发回收,无分代,低STW;
- Python:引用计数法+标记-清除法(解决循环引用)+分代回收;
- C#:.NET CLR,分代回收+并发回收。
- V8引擎的垃圾回收机制具体是如何工作的?
V8引擎的核心背景
V8是谷歌开发的JS引擎,用于Chrome浏览器和Node.js,单线程执行,内存限制严格(32位系统约 1GB,64位系统约1.4GB),垃圾回收机制直接决定JS的运行性能,V8采用分代回收机制,将内存分
为新生代和老生代,针对不同代的对象采用不同的回收算法,兼顾回收效率和内存利用率。
V8内存布局(垃圾回收的基础)
V8将内存分为堆内存和栈内存,垃圾回收仅针对堆内存(栈内存由JS执行栈自动管理,函数执行完即
- 堆内存:存储引用数据类型(Object/Array/Function/RegExp),内存大、访问慢、需GC回收,
是垃圾回收的核心区域。
堆内存进一步分为新生代和老生代:
• 新生代(New Space):占堆内存的10% 左右,存储存活时间短的临时对象(如局部变量、临时
创建的对象),内存小(默认1-8MB),回收频率高、速度快;
• 老生代(Old Space):占堆内存的90% 左右,存储存活时间长的对象(如全局变量、常驻内存的
对象、新生代晋升的对象),内存大,回收频率低、速度慢。
V8垃圾回收的核心流程(分代回收)
- 第一部分:新生代垃圾回收(Scavenge 算法,基于Copying复制算法)
新生代采用Scavenge算法,核心是空间换时间,将新生代内存分为两个大小相等的半区:From区(使用区)和To区(空闲区)。
回收步骤
- 内存分配:新创建的对象优先分配到From区,From区满时触发新生代GC;
- 标记存活对象:从根对象出发,扫描From区,标记可达的存活对象;
- 复制存活对象:将From区的存活对象复制到To区,并按内存地址连续排列,消除内存碎片;
- 清空From区:复制完成后,直接清空From区的所有内存,释放垃圾对象;
- 区交换:From区和To区角色互换,原To区变为新的From区,原From区变为新的To区,等待下一
次GC。
新生代对象的晋升机制(晋升到老生代)
存活对象满足以下任一条件,会从新生代晋升到老生代:
- 存活次数达标:对象在新生代GC中存活超过2次(默认阈值,可配置),说明是长期存活对象;
- 存活次数达标:对象在新生代GC中存活超过2次(默认阈值,可配置),说明是长期存活对象;
- To区内存不足:复制存活对象到To区时,To区空间占用超过50%,直接晋升到老生代(避免To区
快速满溢)。
- 第二部分:老生代垃圾回收(Mark-Sweep + Mark-Compact 算法)
老生代对象特点:数量多、内存大、存活时间长,若使用Scavenge算法,复制成本过高,因此V8采用标记-清除(Mark-Sweep)和标记-整理(Mark-Compact)结合的算法,核心是时间换空间。
- 清除阶段:扫描整个老生代内存,释放未被标记的垃圾对象的内存,将内存归还给操作系统。
- 标记-整理(Mark-Compact)阶段(解决内存碎片)
标记-清除后会产生大量内存碎片,当内存碎片过多,无法分配大对象时,触发标记-整理:
- 标记阶段:与标记-清除的标记阶段一致,标记存活对象;
- 整理阶段:将所有存活对象移动到内存的一端,按连续地址排列;
- 清除阶段:释放存活对象另一端的所有内存,彻底消除内存碎片。
- 第三部分:V8 GC的优化策略(减少STW卡顿)
传统GC会触发STW(Stop The World),暂停JS执行,导致页面卡顿,V8通过以下优化减少卡顿:
- 并发标记(Concurrent Marking):标记阶段与JS执行并行,垃圾回收器后台扫描标记,不暂停
JS执行;
- 增量标记(Incremental Marking):将标记阶段拆分为多个小步骤,穿插在JS执行间隙进行,避
免长时间STW;
- 惰性清除(Lazy Sweeping):清除阶段不一次性清除所有垃圾,而是在JS需要分配内存时,按
需清除部分垃圾,减少单次STW时间;
- 写屏障(Write Barrier):并发标记时,若JS修改对象引用(如新增/删除引用),通过写屏障记
录变化,避免标记错误;
- 并行清理(Parallel Sweeping):清除阶段启用多线程并行清理垃圾,提升清理效率。
- V8 GC的核心总结
- 分代回收:新生代Scavenge(复制),老生代Mark-Sweep+Mark-Compact(标记-清除/整
理);
- 优化核心:通过并发/增量标记、惰性清除,减少STW卡顿,平衡回收效率和用户体验;
- 晋升机制:新生代存活久、To区不足的对象晋升到老生代;
- 适用场景:适配JS单线程模型,针对短期临时对象和长期常驻对象分别优化。
- JavaScript哪些操作可能引起内存泄漏?如何避免?
一、内存泄漏的定义
内存泄漏是指程序中不再使用的内存无法被垃圾回收器(GC)回收,持续占用内存资源,导致内存占用率逐渐升高,进而引发程序运行变慢、页面卡顿,严重时会触发内存溢出(OOM),导致程序崩
全站分卷索引 · 10 卷
- 1第 1 卷
- 2第 2 卷
- ◦ 处理样式优先级(!important > 行内样式 > ID选择器 > 类/伪类/属性选择器 > 标签/伪元素选择
- 关键特性
- 6. 详述浏览器加载和执行JavaScript文件的机制
- JS加载执行核心机制(单线程+阻塞特性)
- • 浏览器解析HTML时,遇到 <script> 标签,立即暂停后续HTML解析,发起HTTP请求加载JS文
- • 可通过属性优化加载:
- ◦ async:异步加载,加载完成后立即执行(执行顺序不确定,不阻塞HTML解析,阻塞渲
- ◦ defer:异步加载,HTML解析完成后、DOMContentLoaded事件前执行(执行顺序与标签
- ◦ type="module":ES6模块加载,默认异步,遵循 defer 规则,支持按需加载、作用域隔
- • V8引擎采用即时编译(JIT):将AST转换为字节码,再将热点代码(频繁执行)编译为机器码,
- 关键特性
- • 异步JS(async/defer/module)仅解除对HTML解析的阻塞,仍会阻塞 DOMContentLoaded 事
- 3第 3 卷
- 4第 4 卷
- 5第 5 卷
- 6第 6 卷
- ✅ 2. 微任务的优先级和执行时机不同
- ✅ 3. 宏任务的类型和优先级不同
- ✅ 4. 页面渲染的差异
- • Node.js事件循环:无页面渲染环节,Node.js是服务端运行环境,无需渲染页面,专注于I/O处
- ✅ 5. 异步API的支持差异
- • 浏览器:支持DOM事件、AJAX、requestAnimationFrame、MutationObserver等前端专属异步
- • Node.js:支持文件I/O、网络I/O、child_process、setImmediate、process.nextTick等服务端专
- ✅ 6. 任务队列的数量差异
- ✅ 核心差异总结表
- 17. 描述process.nextTick在Nodejs事件循环中的执行顺序及其与微任务
- 的关系
- process.nextTick的定义
- 7第 7 卷
- 8第 8 卷
- 9第 9 卷
- 10第 10 卷
- • 不再使用的全局变量,手动置空释放:`window.data = null`(浏览器)、`delete global.data`
- • 避免闭包长期引用大对象,若必须引用,业务结束后手动清空闭包引用:`persistentClosure =
- • DOM元素移除前,通过`removeEventListener`移除对应的事件监听(注意:监听函数必须与绑
- • 组件生命周期销毁时(如React的`componentWillUnmount`、Vue的`beforeUnmount`),批
- • 定时器/计时器使用后,务必通过`clearTimeout`/`clearInterval`清理,尤其是在组件销毁、页
- • 将定时器ID存储在实例属性中,便于后续清理:`this.timer = setTimeout(...)`,销毁时执行
- • 避免在JS中长期缓存DOM元素,优先通过`document.querySelector`动态获取,减少不必要的
- • 异步任务(Promise/AJAX)确保完成:无论成功/失败,都调用`resolve/reject`;使用
- • 内存缓存设置上限和过期机制:使用`lru-cache`等成熟缓存库(自动淘汰最近最少使用的缓
- • 优先使用`WeakMap/WeakSet`作为缓存:它们的键是弱引用,当键对应的对象无其他引用时,会
- • 浏览器端:使用Chrome开发者工具(Memory面板),通过HeapSnapshot(堆快照)对比内存
- • Node.js端:使用`--inspect`参数启动服务,通过Chrome开发者工具调试;使用`clinic.js`、