目前有四种垃圾回收方法
1. 引用计数
- 每被引用一次,就增加1,取消引用,减1,到0时,自动清除该变量
- 问题在于,循环引用会导致变量的引用次数无法清零,也就无法删除该变量
- 例如,a = {a:1} b = {b:2}, a.b = b, b.a = a
- 此时a被b.a引用了,b又被a.b引用了,但是a和b并没有被其他地方引用,理论上应该清理,实际并没有
2. 标记清除
- GC有个根节点root
- 每次触发垃圾回收机制的时候,从root节点开始广度查找,找到所有与root有关联的空间进行标记
- 然后把没有被标记的空间,清除掉
- 这个问题在于,清除掉的空间很分散,无法被合理的利用,造成资源浪费
3. 标记整理
- 由标记清除引申而来,
- 标记结束后,将标记后的空间向一侧移动,未被标记的空间就被覆盖了,所以会造成整理后的空间引用地址发生了改变,移动完成后,将整理后未使用的空间都清除掉。
- 缺点就是整理的过程耗时较长
4. 标记复制
- 标记阶段与之前一样
- 标记完成后,将标记后的内容直接复制到新的内容,然后得到一个与标记整理之后相同的区域,再将原来的区域整体清除掉。
- 最后将复制后的区域当做正在使用的区域。
- 循环往复
- 缺点在于需要浪费一半的内存
V8使用的是标记复制+标记整理
- 首先分为新生代和老生代
- 新生代用来存放体积小,更新频率高的对象
- 老生代用来存放体积大,不易更新的对象
- 新生代采用标记复制的方式,所以一般体积只有64M甚至32M,对半分也只会浪费32M或者16M,当对象占用内存超过分区的1/4时,或者已经被复制过2次以上,就会强制移动至老生代。
- 老生代就比较大,当内存占满时,就会触发标记整理,来清除那些用不到的内存空间。
以上是我的粗浅见解,如有疑问,可以私信或留言!还望大家共同进步!