|
|
|
|
|
|
作者:聚划算前端开发专家 韩璟(花名:业勤)
在JavaScript中,每当我们创建一个对象,都会占用内存,不再使用时,浏览器会自动释放。这种自动化的内存的管理的方式,大大降低了开发对于js内存管理的成本,但也造成了开发人员的JavaScript的内存管理忽视。然而现在,各种单页应用的诞生,各种不同无线终端少的可怜内存分配,交互的复杂性以及流畅性,以及nodejs应用的崛起,又使得JavaScript的内存管理变得重要起来。
在ECMAScript标准中,没有规定任何的内存管理的接口,这使得开发者没有任何的能力来操作内存。但并不代表我们可以不关心内存。
怎样才是一个好的内存管理?简单来说就是做到以下两点使得js的内存的使用保持在较低水平。如果浏览的内存使用过高,往往容易导致浏览器的crash。内存泄漏,导致内存无法被浏览器回收,也是引起内存使用过高的重要原因。
内存的生命周期大致分为三个步骤
JavaScript 在定义变量或者创建一个变量的时候,就完成了内存分配。
// 给数值变量分配内存 // 给字符串分配内存 // 给对象及其包含的值分配内存 // 给数组及其包含的值分配内存(就像对象一样) // 给函数(可调用的对象)分配内存
minor GC,而不被回收的对象就会被移动到年老区。在年老区进行的GC操作,就是
这一节,我们来了解下V8的GC引擎是到底是如何工作的。
在V8中,会把内存分为两个部分:新生区( young generation )和年老区(old generation),新生区的内存空间较小,用于创建和分配新的内存,年老区的空间较大,用于存放存活时间较长的对象。
大部分的对象,会在新生区里面被回收,这个时候发生的GC就是上面讲到的major GC
.
在新生区内,内存还会分为两个区,其中一个是active区,所有的内存分配都发生在active区,当active区满了以后,这个时候就会触发minor GC
,把活的对象移动到另一个半区,并且这些对象最终会移动到年老区。而其余的对象就会被清除,内存进行回收。minor GC
相对于major GC
,触发的更加频繁,并且处理速度也比较快。因为一是内存空间小,相对涉及的对象数也比较少。二是分成了两个半区,避免了内存整理压缩等操作。
下面了解下年老区的处理。在年老区里,当内存使用超出限制时,就会触发major GC
,回收算法就是使用的我们说的标记-清除(mark-sweep)算法,通常来说,标记整个年老区需要很长的时间,这里V8做了优化,支持增量标记,这样就可以把整个标记过程,分成多个小步。在清除完垃圾以后,以避免清除后年老区产生的内存碎片,还要进行内存压缩的操作,这个操作也比较耗时。所以可以看出,major GC
确实要比minor
来张图 回顾下整个的GC流程
我们知道浏览器的最快的渲染频率是60fps,也就是是说每帧的时间间隔是16.6ms,如果浏览器在16.6ms内完成了所有的操作(包括js的运行和渲染),剩下的时间就是空闲时间,在这些空闲时间里面,就会执行GC操作,以便可以把GC操作的影响降到最低。
这里面chrome很多的优化,以保证GC时间不超过空闲时间。例如,
通过内存分配的速度,预测新生区什么时候会满,从而决定什么时候进行minor GC;
年老区进行增量标记,可以使标记操作分散在多段空闲时间里;
年老区里并不是每次GC操作都进行内存整理的工作,同时也会考虑空闲的大小是否足够
以上我们可以看出,浏览器对于GC再不断的优化,并且对于内存的管理,也提供给了我们很多的工具。这也说明了内存管理是越来的越重要了。总结下内存相关的东西,以便可以解决项目里内存的问题。然而,每当页面crash的时候,内心还是捉急和懵逼的。