四.垃圾回收
1.javascript 中的垃圾收集
- 程序的运行需要内存,只要程序要求,操作系统就必须提供内存
- javascript 使用自动内存管理,这被称为"垃圾回收机制"
- 优点是可以简化开发、节省代码
- 缺点是无法完整的掌握内存的分配与回收的具体过程
V8 的垃圾回收机制
V8 是基于分代的垃圾回收
不同代垃圾回收机制也不一样
按存货的时间分为新生代和老生代
分代
年龄小的是新生代,由 From 区域和 To 区域两个区域组成
- 在 64 位系统里,新生代内存时 32M,From 区域和 To 区域各占用 16M
- 在 32 位系统里,新生代内存时 16M,From 区域和 To 区域各占用 8M
- 年龄大的是老生代,默认情况下
- 64 位系统下老生代内存时 1400M
- 32 位系统下老生代内存时 700M
引用计数
- 语言引擎有一张引用表,保存了内存里面所有的资源的引用次数
- 如果一个值的引用次数是 0,就表示这个值不再用到了,因此可以将这块内存释放。
新生代垃圾回收
- 新生代区域一分为二,每个 16M,一个使用,一个空闲
- 开始垃圾回收的时候,会检查 FROM 区域中的存活对象,如果还活着,拷贝到 TO 空间,完成后释放空间。
- 完成后 FROM 和 TO 互换
- 新生代扫描的时候是一种广度优先的扫描策略
- 新生代的空间小,存活对象少
- 当一个对象经历过多次的垃圾回收依然存活的时候,生存周期比较长的对象会被移动到老生代,这个移动的过程被称为晋升或者升级
老生代
- mark-Sweep(标记清除)mark-compact(标记整理)
- 老生代空间大,大部分都是活着的对象,GC 耗时比较长
- 在 GC 期间无法响应,STOP-THE-WORLD
- V8 有一个优化方案,增量处理,把一个大暂停换成多个小暂停 INCREMENT-GC
mark-sweep(标记清除)
- 标记活着的对象,随后清除在标记阶段没有标记的对象,只清理死亡对象
- 问题在于清除后会出现内存不连续的情况,这种内存碎片会对后续的内存分配产生影响
- 如果要分配一个大对象,碎片空间无法分配
mark-compact(标记整理)
- 标记死亡后会对对象进行整理,活着的对象向左移动,移动完成后直接清理掉边界外的内存
incrementtal marking 增量标记
- 以上三种回收是都需要暂停程序执行,收集完成后才能恢复, STOP-THE-WORLD 在新生代影响不大,但是老生带影响就非常大了。
- 增量标记就是把标记改为增量标记,把一口气的停顿拆分成了多个小步骤,做完一步程序运行一会儿,垃圾回收和应用程序运行交替进行,停顿是按可以减少到 1/6 左右,包括垃圾会后的占用时间