js垃圾回收
# js 垃圾回收
浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且 GC 时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。
垃圾:
一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。
JavaScript 中会被判定为垃圾的情形如下:
对象不再被引用;
对象不能从根上访问到;
垃圾回收算法:
引用计数
标记清除
标记整理
分代回收
# 1.引用计数
语言引擎有一张"引用表",保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是 0,就表示这个值不再用到了,因此可以将这块内存释放。
如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
缺点:
引用计数法是最初级的垃圾收集算法,如果某对象没有其他对象指向它了,那就说明它可以被回收。但是它无法处理循环引用的问题。
循环引用时,两个对象都至少被引用了一次,将不能自动被回收。所以导致,我们常讲的内存泄露。
引用计数算法其实还有一个比较大的缺点,就是我们需要单独拿出一片空间去维护每个变量的引用计数,这对于比较大的程序,空间开销还是比较大的。
引用计数算法优点:
引用计数为零时,发现垃圾立即回收;
最大限度减少程序暂停;
引用计数算法缺点:
无法回收循环引用的对象;
空间开销比较大;
# 2.标记清除
标记清除算法由两个阶段组成:
第一阶段:标记阶段,标记所有的可访问对象.
第二阶段:清除阶段,垃圾收集算法扫描堆并回收所有的未标记对象.
流程:
垃圾收集器找到所有的根(在 JS 中就是全局对象),并“标记”(记住)它们。
然后它遍历并“标记”来自它们的所有引用。
然后它遍历标记的对象并标记 它们的 引用。所有被遍历到的对象都会被记住,以免将来再次遍历到同一个对象。
……如此操作,直到所有可达的(从根部)引用都被访问到。
没有被标记的对象都会被删除。
标记清除算法有两个很明显的缺点:
● 内存碎片化,在清除之后,剩余的对象内存位置是不变的,会导致空闲内存空间是不连续的,容易出现很多空闲内存块(内存碎片),还可能会出现分配所需内存过大的对象时找不到合适的块。
● 分配速度慢,因为即便是使用 First-fit 策略,其操作仍是一个 O(n) 的操作,最坏情况是每次都要遍历到最后,同时因为碎片化,大对象的分配效率会更慢。
循环引用不再是问题了,两个循环引用的对象在垃圾收集时从全局对象出发无法再获取他们的引用。 因此,他们将会被垃圾回收器回收。
# 3.标记整理(Mark-Compact)
为了解决内存碎片化的问题,提高对内存的利用,引入了标记整理算法。
标记整理可以看做是标记清除的增强。
标记阶段的操作和标记清除一致。清除阶段会先执行整理,移动对象位置,将存活的对象移动到一边,然后再清理端边界外的内存。
标记整理的缺点是:移动对象位置,不会立即回收对象,回收的效率比较慢。
# JavaScript 引擎对垃圾回收应用了许多优化,使其运行得更快,并且不影响执行。
分代回收——对象分为两组:“新对象”和“旧对象”。许多对象出现,完成它们的工作并迅速结 ,它们很快就会被清理干净。那些活得足够久的对象,会变“老”,并且很少接受检查。
增量回收——如果有很多对象,并且我们试图一次遍历并标记整个对象集,那么可能会花费一些时间,并在执行中会有一定的延迟。因此,引擎试图将垃圾回收分解为多个部分。然后,各个部分分别执行。这需要额外的标记来跟踪变化,这样有很多微小的延迟,而不是很大的延迟。
空闲时间收集——垃圾回收器只在 CPU 空闲时运行,以减少对执行的可能影响。
# V8 的 GC 机制
V8 中的垃圾回收主要使用的是 分代回收 (Generational collection)机制。
V8 中将堆内存分为 新生代 和 老生代 两区域,采用不同的垃圾回收器也就是不同的策略管理垃圾回收。
(1)新生代:对象的存活时间较短。新生对象或只经过一次垃圾回收的对象。简单来说就是新产生的对象,通常只支持 1 ~ 8M 的容量,
(2)老生代:对象存活时间较长。经历过一次或多次垃圾回收的对象(常驻内存的对象)。简单来说就是经历过新生代垃圾回收后还存活下来的对象,容量通常比较大。
回收新生代对象主要采用复制算法(Scavenge 算法)加标记整理算法。而 Scavenge 算法的具体实现,主要采用了 Cheney 算法。
回收老生代对象主要采用标记清除、标记整理、增量标记算法,主要使用标记清除算法,只有在内存分配不足时,采用标记整理算法。
首先使用标记清除完成垃圾空间的回收;
采用标记整理进行空间优化;
采用增量标记进行效率优化;
新生代和老生代回收对比
新生代由于占用空间比较少,采用空间换时间机制。
老生代区域空间比较大,不太适合大量的复制算法和标记整理,所以最常用的是标记清除算法,为了就是让全停顿的时间尽量减少。
# js 垃圾回收问题
# 1.什么是垃圾
一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。
# 2. 垃圾回收时会阻塞 js 的运行么
javascript 的垃圾回收会对 javascript 执行线程形成阻塞