微信公众号:[前端一锅煮]
一点技术、一点思考。
JavaScript 引擎的内存空间主要分为栈和堆。前端
V8 的垃圾回收策略主要基于分代式垃圾回收机制。按照对象的存活时间将内存的垃圾回收进行不一样分代,而后分别对不一样分代的内存使用最适合的算法。主要分为新生代和老生代,有标记清除、标记整理、增量标记等方法。vue
栈是临时存储空间,主要存储局部变量和函数调用。node
基本类型赋值(Number, Boolean, String, Null, Undefined, Symbol, BigInt),系统会为新的变量在栈内存中分配一个新值。web
引用类型赋值,系统会为新的变量在栈内存中分配一个值,这个值仅仅是指向同一个对象的引用,和原对象指向的都是堆内存中的同一个对象。算法
对于函数,解释器建立了”调用栈“来记录函数的调用过程。每调用一个函数,解释器就把该函数添加进调用栈,解释器会为被添加进来的函数建立一个栈帧(用来保存函数的局部变量以及执行语句)并当即执行。若是正在执行的函数还调用了其余函数,新函数会继续被添加进入调用栈。函数执行完成,对应的栈帧当即被销毁。vue-cli
使用 console.trace() 向 web 控制台输出一个堆栈跟踪。浏览器
浏览器开发者工具进行断点调试。服务器
栈虽然很轻量,在使用时建立,使用结束后销毁,可是不是能够无限增加的,被分配的调用栈空间被占满时,就会引发”栈溢出“的错误。微信
(function foo() { foo() })()
Maximum call stack size exceeded.ide
JavaScript 引擎须要用栈来维护程序执行期间的上下文的状态,若是栈空间大了的话,全部数据都存放在栈空间里面,会影响到上下文切换的效率,进而影响整个程序的执行效率。
堆空间存储的数据比较复杂,大体能够划分为 5 个区域:
新生代内存是临时分配的内存,存活时间短,老生代内存是常驻内存,存活时间长。
新生代内存中的垃圾回收主要经过 Scavenge 算法进行,具体实现时主要采用 Cheney 算法。
Cheney 将内存空间一分为二,一块叫作 From 正在使用的内存,另外一块叫作 To 目前闲置的内存。
Scavenge GC算法:
简而言之,在垃圾回收的过程当中,将存活对象在两个空间之间进行复制。
Scavenge 是典型的牺牲空间换取时间的算法,缺点是只能使用堆内存中的一半,这是由划分空间和复制机制所决定的。但因为只复制存活的对象,而且对于生命周期短的场景存活对象只占少部分,因此它在时间效率上有优异的表现。
V8 在老生代中主要采 用了 Mark-Sweep 和 Mark-Compact 相结合的方式进行垃圾回收。
当一个对象通过屡次复制依然存在时,它将会被认为是生命周期较长的对象,这种对象会被移到老生代中,采用新的算法进行管理,这种移动称之为“晋级”。
对象晋级的条件主要有两个:
已经经历过一次 Scavenge 回收
To(闲置内存)空间的内存不足75%
标记清除,分为标记和清除两个阶段。在标记阶段遍历堆中的全部对象,并标记活着的对象,在随后的清除阶段中,只清除没有被标记的对象。能够看出,Scavenge 中只复制活着的对象,而 Mark-Sweep 只清理死亡对象。
标记清除最大的问题是在进行一次标记清除回收后,内存空间会出现不连续的状态。这种内存碎片会对后续的内存分配形成问题,由于极可能出现须要分配一个大对象的状况,这时全部的碎片空间都没法完成这次分配,就会提早触发垃圾回收,而此次回收是没必要要的。为了解决标记清除的内存碎片问题,标记整理(Mark-Compact)被提出来。
Mark-Compact 是标记整理的意思,在 Mark-Sweep 的基础上演变而来。
标记整理对待未存活对象不是当即回收,而是将存活对象移动到一边,而后直接清掉端边界之外的内存。
为了不出现 JavaScript 应用程序与垃圾回收器看到的不一致的状况,进行垃圾回收的时候,都须要将正在运行的程序停下来,等待垃圾回收执行完成以后再回复程序的执行,这种现象称为“全停顿”。若是须要回收的数据过多,那么全停顿的时候就会比较长,会影响其余程序的正常执行。
为了不垃圾回收时间过长影响其余程序的执行,V8将标记过程分红一个个小的子标记过程,同时让垃圾回收和JavaScript应用逻辑代码交替执行,直到标记阶段完成。咱们称这个过程为增量标记算法。
通俗理解,就是将本来一口气完成的标记任务分为了不少小的部分去完成, 每完成一个小任务就停一会, 让 js 逻辑执行一会, 而后再继续执行下面的部分。
从 V8 垃圾回收机制能够看到,垃圾回收是一件很是耗时的事情, 以 1.5GB 的垃圾回收堆内存为例,V8 作一次小的垃圾回收须要 50ms 以上,作一次非增量式的垃圾回收甚至要 1s 以上,因此要作限制。
新生代设计为一个较小的内存空间是合理的,而老生代空间过大对于垃圾回收并没有特别意义。V8 对内存限制的设置对于 Chrome 浏览器这种每一个选项卡页面使用一个 V8 实例而言,内存的使用是绰绰有余了。对于 Node 编写的服务器端来讲,内存限制也并不影响正常场景下的使用。可是对于 V8 的垃圾回收特色和js 在单线程上的执行状况,垃圾回收是影响性能的因素之一。想要高性能的执行效率,须要注意让垃圾回收尽可能少地进行,尤为是全堆垃圾回收。
vue-cli 打包内存溢出,修改内存限制
node_modules/.bin vue-cli-service #!/usr/bin/env node --max_old_space_size=4096
调整老生代内存限制,单位mb
node --max-old-space-size=2048 build/build.js
调整新生代内存限制,单位kb
node --max-new-space-size=2048 build/build.js