浏览器垃圾
在JavaScript中,数据类型分为两类,简单类型和引用类型,对于简单类型,内存是保存在栈(stack)空间中,复杂数据类型,内存是保存在堆(heap)空间中。
- 基本类型:这些类型在内存中分别占有固定大小的空间,他们的值保存在栈空间,我们通过按值来访问的
- 引用类型:引用类型,值大小不固定,栈内存中存放地址指向堆内存中的对象。是按引用访问的。
浏览器垃圾:当一个对象(在堆-Heap里)没有任何变量或属性(在栈中)对它进行引用,此时将永远无法操作此对象。
按照V8浏览器的标准,垃圾又分2种类型:
- 新生代的垃圾:生存时间短的对象(如内部声明的变量,或者块级作用域中的变量等),使用完马上可以回收。
- 老生代的垃圾:生存时间久的对象(如挂载在windows下面的变量、JS一些API、DOM、闭包等),持久性强,需要等待用完才能回收。
垃圾过多后果: 内存移出,内存泄露.
垃圾回收机制
垃圾回收是一种自动的内存管理机制。当计算机上的动态内存不再需要时,就应该予以释放。
需要注意的是,自动的意思是浏览器可以自动帮助我们回收内存垃圾,但并不代表我们不用关心内存管理,如果操作不当,JavaScript中仍然会出现内存溢出的情况,造成系统崩溃。
由于字符串,数组,对象等都没有固定大小,因此需要当它们大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串,数组或对象时,解释器都必须分配内存来存储那个实体。
JavaScript解释器可以检测到何时程序不在使用一个对象了,当它确定这个对象是无用的时候,他就知道不再需要这个对象了,就可以把它占用的内存释放掉了。
浏览器通常采用的垃圾回收有两种方法:标记清除,引用计数。
垃圾回收器有2种
Major GC( 主垃圾回收器 ):主要负责老生代的垃圾回收;
Minor GC( 副垃圾回收器 ):主要负责新生代的垃圾回收;
Major GC
负责老生代的垃圾回收,采用了 Mark-Sweep(标记清除) 和 Mark-Compact(标记整理) 算法。
标记清除
首先是标记。
从一组根元素开始,递归遍历这组根元素。
在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。
然后是垃圾清除。
多次标记-清除后,会产生大量不连续的内存碎片,需要进行内存整理。
老生代的内存中产生了很多内存碎片,若不清理这些内存碎片,如果出现需要分配一个大对象的时候,这时所有的碎片空间都完全无法完成分配,就会提前触发垃圾回收,而这次回收其实不是必要的。
标记整理
为了解决内存碎片问题,Mark-Compact被提出,它是在 Mark-Sweep的基础上演进而来的,相比Mark-Sweep,Mark-Compact添加了活动对象整理阶段,将所有的活动对象往一端移动,移动完成后,直接清理掉边界外的内存。
Minor GC
负责新生代的垃圾回收,内存占用比较小。
新生代被分为两个区域:一般是对象区域,一半是空闲区域。
新加入的对象都被放入对象区域,等对象区域快满的时候,会执行一次垃圾清理。
先给对象区域所有垃圾做标记。
标记完成后,存活的对象被复制到空闲区域,并且将他们有序的排列一遍。无需碎片整理
复制完成后,对象区域会和空闲区域进行对调。将空闲区域中存活的对象放入对象区域里。
因为副垃圾回收器操作比较频繁,所以为了执行效率,一般新生区的空间会被设置得比较小。
一旦检测到空间装满了,就执行垃圾回收。
垃圾回收的时机以及方案
由于垃圾回收是在JS引擎中进行的,而Mark-Compact算法在执行过程中需要移动对象,为了避免JavaScript应用逻辑和垃圾回收器的内存资源竞争导致的不一致性问题,垃圾回收器会将JavaScript应用暂停,这个过程,也被称为全停顿(stop-the-world)。
分代收集
一句话总结分代回收就是:将堆分为新生代与老生代,多回收新生代,少回收老生代。这样就减少了每次需遍历的对象,从而减少每次垃圾回收的耗时。
增量收集
如果脚本中有许多对象,引擎一次性遍历整个对象,会造成一个长时间暂停。
所以引擎将垃圾收集工作分成更小的块,每次处理一部分,多次处理。
这样就解决了长时间停顿的问题。
闲时收集
垃圾收集器只会在 CPU 空闲时尝试运行,以减少可能对代码执行的影响。
总结
浏览器中不同类型变量的内存都是何时释放?
Javascritp 中类型:值类型,引用类型。
- 引用类型
在没有引用之后,通过 V8 自动回收。- 值类型
如果处于闭包的情况下,要等闭包没有引用才会被 V8 回收。
非闭包的情况下,等待 V8 的新生代切换的时候回收。
闭包会导致内存泄露吗?
内存泄露是指你「用不到」(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
闭包里面的变量就是我们需要的变量,不能说是内存泄露。
weakMap weakSet 和 Map Set 有什么区别?
在 ES6 中为我们新增了两个数据结构 WeakMap、WeakSet,就是为了解决内存泄漏的问题。
它的键名所引用的对象都是弱引用,就是垃圾回收机制遍历的时候不考虑该引用。
只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。
也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。