JS引擎V8的内存回收机制与内存限制(标记清除法)
原创真的137 最后发布于2019-03-12 13:48:37 阅读数 166 收藏
展开
在Node中通过JavaScript使用内存时会发现只能使用部分呢内存(64位下位1.4GB,32位系统下位0.7GB),这样的限制使得Node无法操作大内存对象。造成这个问题的原因在于Node基于V8构建,所以在Node中使用的JavaScript对象基本上都是通过V8自己的方式来进行分配和管理的。
在V8中,所有的JavaScript对象都是通过堆来分配的。当我们在代码中声明变量并赋值的时候,所使用的对象就会分配在堆中。如果已申请的堆空闲内存不够分配新的对象,将继续申请堆内存,直到堆的大小超过V8的限制为止。V8限制堆大小的原因在于V8的垃圾回收机制的限制。
V8的垃圾回收机制
V8主要的垃圾回收算法
V8的垃圾回收策略主要基于分代式垃圾回收机制
(ps:本文参考《深入浅出Nodejs》)
1、V8的内存分代
在V8中,主要将内存分为新生代和老生代。新生代中的对象位存活时间较短的对象,老生代中的对象为存活时间较长或者常驻内存的对象。
新生代和老生代的内存空间在一开始就是指定的。
2、Scavenge算法
在分代的基础上,新生代中的对象主要通过Scavenge算法进行垃圾回收。
Scavenge的具体实现中,主要采用了Cheney算法。
Cheney算法是一种采用复制的方式实现的垃圾回收算法。它将堆内存一分为二,每一部分空间成为semispace。在这两个semispace空间中,只有一个处于使用中,另一个处于闲置状态。处于使用状态的semispace空间叫做From空间,处于闲置状态的semispace空间叫to空间。当我们分配对象的时候,先从From空间空间中进行分配。当开始进行垃圾回收的时,会检查From空间中的存活对象,这些存活对象将被复制到To空间中,而非存活对象占用的空间将会被释放。完成复制后,From和To空间的角色发生对换。
简而言之,就是通过将存活对象在两个semispace空间之间进行复制。
从前面的机制来看,Scavenge是一种牺牲空间换时间的算法,所以无法大规模地应用到所有的垃圾回收中,但是Scavenge非常使用对象生命周期比较短的场景也就是适合用在新生代中。
实际上的堆内存是新生代的两个semispace空间大小和老生代所使用内存大小之和
。
当一个对象经过多次复制仍然存活的时候,它就会被认为是身影周期比较长的对象,这种生命周期比较长的对象随后会被移动到新生代中,采用新的算法进行管理。从新生代到老生代的过程成为晋升。
对象晋升的两个条件
对象是否经历过Scavenge回收(通过检测内存地址是否改变)
对象从From空间中复制到To空间中时,先判断这个对象是否经历过一次Scavenge回收,如果经历过,会从From空间复制到老生代空间,如果没有经历,则复制到To空间。
To空间的内存占用比是否超过闲置
当一个对象从From空间复制到To空间时候,如果To空间使用率超过25%,则将该对象直接晋升到老生代中
设置25%是因为当Scavenge回收结束之后,T空间会变成From空间,接下俩的内存分配会在这个空间进行,如果占比过高,会影响后续的内存分配
3、Mark-Sweep & Mark-Compact
老生代采用Mark-Sweep & Mark-Compact而不采用Scavenge算法的原因:采用Scavenge会导致再一半的空间浪费,第二是存活对象较多,复制存活对象的效率将会很低.
Mark-Sweep是标记清除的意思,它分为标记和清除两个阶段。Mark-Sweep在标记阶段遍历堆中所有对象,并标记活着的对象,在随后的清除阶段中,只清除没有被标记的对象。可以看出,Scavenge只复制活着的对象,而Mark-Sweep只清除死亡对象。活对象在新生代中只占较小部分,死对象在老生代中只占较小部分,这是两种回收方式能高效处理的原因。
Mark-Sweep的最大问题在于一次标记清除之后,内存空间会出现不连续的状态,这种内存碎片会对后续的内存分配造成问题,当无法完成此次分配的时候,就会触发一次垃圾回收。
Mark-Compact是为解决Mark-Sweep内存碎片提出来的。Mark-Compact是标记的意思。他们的差别在与标记为死亡后,在整理的过程中,将活着的对象往一端移动,移动完成后,移动完成后,直接清除掉边界的内存。
Mark-Sweep和Mark-Compact这两种策略是递进关系,但是在V8中两者是结合使用的。
垃圾回收造成的停顿
垃圾回收的这三种算法都需要将应用逻辑暂停下来,待执行完垃圾回收后再恢复执行应用逻辑。当v8的分代式垃圾回收中,新生代造成的停顿比老生代造成的停顿少的多。为了减少这种影响,引入了增量标记、延迟标记、增量式整理等。计划引入并行标记和并行处理。
V8对内存闲置的设置对于Chrome浏览器这种选项卡页面使用一个V8实例来说,内存的使用是绰绰有余的,而对Node编写的服务端来说,内存闲置也不影响正常场景下的使用。想要高性能的执行效率,注意让垃圾回收尽量少地进行。
以Web服务器中的会话实现为例,一般通过内存来存储,但在访问量大的时候会导致老生代中饭呢的存活对象剧增,不仅造成清理/整理过程费时,还会造成内存紧张,甚至溢出。
js垃圾回收也有另一种方法——引用计数法:可参考引用计数法和标记清除法
更多资料可以参考
并行标记
————————————————
版权声明:本文为CSDN博主「真的137」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38601104/article/details/88417577
原文地址:https://www.cnblogs.com/chargeworld/p/12237039.html