垃圾回收——Garbage Collection
栈内存由操作系统管理,而GC都是指堆内存。
主流引擎:
Trident内核:IE,360,搜狗,TT,The World,MaxThon
Gecko内核:NetScape6及以上,FF,MoziliaSuite/SeaMonkey
Presto内核:Opera7及以上,Opera原为Presto,现为Blink
WebKit内核:Safari,Chrome[Chrome的Blink为WebKit的分支]
现在基本的浏览器JavaScript引擎(如V8 和 SpiderMonkey)都实现了GC。
简单介绍三种常见GC算法:
1、Mark-and-Sweep 标记清理
标记变量状态,对于处于执行期上下文的标记为活跃,其余标记为“待清理”。随后,垃圾回收器将标有“待清理”的变量统一清理,释放内存。
1、标记
2、清理
2、Mark-Compact 标记整理
使用标记整理,可以解决内存碎片化问题,提高内存使用效率。该算法标记阶段耗时严重,易阻塞主线程。
将标记的活跃对象往内存的一端移动,此过程可能会改变内存中对象地址。
1、标记
2、整理
3、清理
3、Reference-counting 引用计数
此算法已经废弃,标记对象引用次数,将计数为0的清理
1、计数
2、清理
此算法无法解决循环引用的情况,容易造成内存泄漏。
V8中的GC
V8是Google开源的C++编写高性能JavaScript引擎
将堆内存(Heap memory)分成几个区域
主要的如下:
New Space: Cheney算法,内存分两块,一个使用状态:From空间, 一个闲置状态:To空间。广度优先算法,查找From中活跃的,向To空间转移。遍历算法是广度优先算法。
Old Space:Mark-Compact算法,为解决标记耗时问题,采用增量标记(Incremental marking)特性。将标记工作分成多个小段,夹杂在主线程逻辑中。
增量标记需要通知垃圾回收器更新标记,通知也需要消耗成本。
另外 V8 使用 Worker thread 实现了 平行标记,并行标记,也是解决标记耗时的手段。
隐藏类
每一个对象都有一个隐藏类,用于提升对象性能。记录对象的特征和属性关系映射。
我们创建对象时,拥有完全相同特征的对象可以共享一个隐藏类。当然隐藏类也需要占用内存空间。
我们要尽量避免动态增删对象属性操作,在构造函数内一次性声明所有需要用到的属性,如果不需要某属性,可以置null。
相同名称的属性尽量按照相同顺序声明,可以尽可能地让更多对象共享相同的隐藏类。即使不能共享隐藏类,也可以减少隐藏类分支的创建。
参考文章:
《JavaScript 高级程序设计(第4版)》
https://liujiacai.net/blog/2018/07/08/mark-sweep/