# 垃圾回收机制
引用计数
广度优先 栈区
内存泄露 闭包
# 浏览器
- 浏览器怎样进行垃圾回收
- 浏览器中不同类型变量的内存都是何时释放
- 哪些情况会导致内存泄露,如何避免
- weakMap weakSet 和 Map Set 有什么区别
# 什么是垃圾数据
let dog = new Object();
dog.a = new Array(1);
dog.a = "b";
这时候的 Array 就成了不被使用的数据,专业名词叫[不可达]的数据。 这就需要回收的垃圾数据。
# 垃圾回收算法
# 第一步:标记空间中[可达]值
V8 采用的是可达性(reachability)算法来判断堆中的对象应不应该被回收。 这个算法的思路是:
- 从根节点(Root)出发,遍历所有的对象
- 可以遍历到的对象,是可达的(reachable)
- 没有被遍历到的对象,是不可达的(unreachable)
在浏览器环境下,根节点很多,主要包括
- 全局变量 window,位于每个 iframe 中
- 文档 DOM 树
- 存放在栈上的变量
- 。。。
这些根节点不是垃圾,不可能被回收
# 第二步:回收[不可达]
的值所占据的内存
在所有标记完成之后,统一清理内存中所有不可达对象
# 第三步:做内存整理
- 在频繁回收对象后,内存中就会存在大量不连续空间,专业名词叫
内存碎片
- 当内存中出现大量的内存碎片,如果需要分配较大的连续内存时,就有可能出现内存不足的情况
- 所以最后一步是整理内存碎片(但这步是可选的,因为有的垃圾回收器不会产生内存碎片,比如接下来我们要介绍的副垃圾回收器)
# 什么时候垃圾回收
浏览器进行垃圾回收的时候,会暂停 JavaScript 脚本,等垃圾回收完毕再继续执行。
对于普通应用,这样没什么问题,但对于 js 游戏、动画对连贯性要求比较高的应用,如果暂停时间很长就会造成页面的卡顿。
# 分代收集
浏览器将数据分为两种,一种是临时
对象,一种是长久
对象。
- 临时对象
- 大部分对象在内存中存活的时间很短
- 比如函数内部声明的变量,或块级作用域中的变量。当函数或代码块执行结束后,作用域中定义的变量就会被销毁。
- 这类对象很快就会变的不可访问,应该快点回收
- 长久对象
- 生命周期很长的对象,比如全局的 window、dom、web API 等等
- 这类对象可以慢点回收
这两种对象对象不同的回收策略,所以,v8 把堆分为新时代和老生代的两个区域。新时代中存放临时对象,老生代中存放持久对象。并且让副垃圾回收器、主垃圾回收器,分别负责新生代和老生代的垃圾回收。
# 主垃圾回收器
负责老生代的垃圾回收,有两个特定
- 对象占用空间大
- 对象存活时间长
它使用标记-清除
的算法执行垃圾回收
- 首先是标记
- 从一组根元素开始,递归遍历这组根元素
- 在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素可以判断为垃圾数据
- 直接将标记为垃圾的数据清理掉
- 多次标记-清除,会产生大量不连续的内存碎片,需要进行内存整理
# 副垃圾回收器
负责新生代的垃圾回收,一般只支持 1-8M 的容量 新生代被分为两个区域(semisspace):一半是对象区域(From),一半是空闲区域(To) 新加入的对象都被放入对象区域,等对象区域快满的时候,会执行一次垃圾清理
- 先给对象区域所有垃圾做标记
- 标记完成后,存活的对象被复制到空闲区域,并且将他们有序的排列一遍
- 副垃圾回收器没有碎片整理,因为空闲区域里此时是有序的,没有碎片,也就是不需要整理了。
- 复制完成后,对象区域会和空闲区域进行对调,将空闲区域中存活的对象放入对象区域中
- 因为副垃圾回收器操作比较频繁,所以为了执行效率,一般新生代的空闲会被设置的比较小。
- 是典型的空间换时间的方法
# 分代收集
将堆分为新生代和老生代,多回收新生代,少回收老生代。-> 这样就减少每次需要遍历的对象,从而减少每次垃圾回收的耗时
# Scavenge 算法
在分代基础上,新生代的对象主要通过 Scavenge 算法进行垃圾回收,在具体实现时主要采用 cheney 算法,cheney 算法是一种采用复制的方法实现垃圾回收算法。
# markSweep 和 markCompact
老生代使用的方法
# 增量收集
如果脚本中有许多对象,引擎一次性遍历整个对象,会造成一个长时间的暂停。所以引擎将垃圾收集工作分为更小的块,每次处理一部分,多次处理。
# 闲时收集
垃圾收集器只会在 CPU 空间时尝试运行,以减少可能对代码执行的影响。
# 面试题
- 浏览器是怎么进行垃圾回收的
- 什么是垃圾回收
- 不在需要的,即是垃圾,全局变量随时可能用到,所以不一定是垃圾
- 如何捡垃圾
- 从根节点出发,遍历所有对象,可达的不是垃圾,不可达的是垃圾
- 回收不可达的值所占据的内存
- 做内存整理
- 什么时候捡垃圾
- 垃圾回收会暂停浏览器的运行,所以采用分代收集、增量收集、闲时收集的策略
- 什么是垃圾回收
- 浏览器中不同类型变量的内存都是何时释放的
- JavaScript 中类型:值类型和引用类型
- 引用类型: 在没有引用之后,V8 自动回收
- 值类型:
- 如果处于闭包,要等闭包没有被引用才会被 v8 回收
- 非闭包的情况下,等待 V8 的新时代的切换的时候回收
- JavaScript 中类型:值类型和引用类型
- 哪些情况会造成内存泄露,如何避免
- 无限制增长的数组
- 无限制设置属性和值
- 任何模块内的私有变量和方法均是永驻内存的 a=null
- 大循环,无 GC(垃圾回收机制)机会
- weakMap weakSet 和 Map Set 有什么区别
- 因为是弱引用,所有可以解决内存泄露的问题
- weakMap weakSet 的键名所引用对象都是弱引用,就是垃圾回收机制遍历的时候不考虑该引用
- 只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存
# ESP
ESP(Extended Stack Pointer)为扩展栈指针寄存器,是指针寄存器的一种,用于存放函数栈顶指针。与之对应的是 EBP(Extended Stack Point),扩展基指针寄存器,也被称为帧指针寄存器,用于存放函数栈底指针。
# node
node 使用 JavaScript 在服务端操作最大内存对象受到一定限制(堆区),64 位系统下约为 1.4GB,32 位系统约 0.7GB,新生代 64 位是 32M,32 位是 16M
> node --huge-max-old-generation-size index.js
# node 内存泄露分析
node-inspector
console.log("Sever PID", process.pid);
sudo node --inspect app.js
top -pid 2322
← 前端路由原理及实现方式 跨域问题 →