js中的垃圾回收站机制
JavaScript引擎可以进行自动内存管理——垃圾回收站机制
垃圾回收站机制是浏览器JS引擎的内存管理机制,该机制会定期对不再使用的变量进行内存的回收
JavaScript垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行
1. 什么是不再使用的变量
不再使用的变量即变量的生命周期结束,它所依赖的执行环境被销毁,当然只可能是局部变量
因为全局变量的生命周期是当当前页面关闭才会结束
也不可能闭包,闭包是由于局部变量被函数外部全局变量所使用,所以也会存在于内存当中,可以手动释放
2. 垃圾回收站的实现方式
-
标记清除法
- 变量进入执行环境,标记为“进入环境”
- 变量离开执行环境,标记为“离开环境”,而标记为“离开环境”的变量是可以被回收的
垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包)。在这些完成之后仍存在标记的就是要删除的变量了,因为环境中的变量已经无法访问到这些变量了,然后GC会回收这些变量的内存
现在基本上所有浏览器采用的都是标记清除的方式
-
引用计数
统计引用类型变量声明后被引用的次数
当引用次数为0时,变量将被回收
在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时候,说明没有变量在使用,这个值没法被访问了(不可达),因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间
引用计数的缺点: 存在循环引用
function fn() { let f = {}; let p = {}; f.prop = p; p.prop = f; }
f 和 g循环引用对方,遇到这种问题可以通过手动释放解决
p.prop = null; f.prop = null;
3. GC的挑战
和其他语言一样,javascript的GC策略也无法避免一个问题:GC时,停止响应其他操作,这是为了安全考虑。而Javascript的GC在100ms甚至以上,对一般的应用还好,但对于JS游戏,动画对连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免GC造成的长时间停止响应
-
分代回收
这个和Java回收策略思想是一致的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时
但是对于tenured generation对象,有额外的开销,比如把它从young generation迁移到tenured generation;另外,如果被引用了,那引用的指向也需要修改
-
增量GC
这个方案的思想很简单,就是“每次处理一点,下次再处理一点,如此类推”
这种方案,虽然耗时短,但中断较多,带来了上下文切换频繁的问题