js运行的宿主环境不同,相应的对内存治理的要求也不一样,当宿主环境是浏览器时,由于网页的运行时间短,且只运行在用户的机器上(相当于分布式),即使内存使用过多或者存在一定的内存泄漏,也并不会对终端用户产生太大的影响。当宿主环境编程服务器(Node)时,情况就大不相同了,本身代码运行在固定的几台机器(集中式)上,而且运行的时间是长时间运行,一旦内存治理不好出现了内存膨胀甚至是内存泄漏的情况的话,就会出现服务器端响应时间变长甚至是服务crash的情况。

Nodejs是基于V8构建的,所以在Node中使用的JavaScript对象基本上(Buffer就不是)都是通过V8来进行分配和管理的。V8在占用内存大小上做了限制(64位操作系统,单个Node进程可使用的最大堆内存大小约为1.5GB)。即使服务器的内存很大,但是由于V8的这种限制,导致Node无法充分利用服务器的资源。即便如此,为什么V8要做这样的限制呢?做这样限制的原因其实是与垃圾回收机制相关,以1.5GB的垃圾回收堆内存堆为例,V8做一次小的垃圾回收需要50ms以上,做一次全量的垃圾回收甚至要1s以上,要知道垃圾回收过程中JavaScript线程是要处于暂停执行的状态,太长的暂行时间对于后端服务的性能是会产生较大影响的,所以出于这方面考虑,V8对堆内存做了限制。即便如此,V8还是提供了可以自定义堆内存大小的方式(--max-old-pace-size),old-space代表老生代、new-space代表新生代。

node --max-old-space-size=xxx index.js  //单位为MB
// 之前还可以通过-max-new-space-size来定义新生代堆大小,现在已经不可以了

当由于内存泄漏导致服务器一直频繁重启的时候,建议先调大堆内存大小来为定位问题争取时间,毕竟服务响应慢总比直接返回错误页对于用户而言会更好接受一点。

为什么需要老生代和新生代?

老生代和新生代其实是分代式垃圾回收机制里面的不同的分代,因为没有一种垃圾回收算法能够胜任所有的场景,不同的对象生存周期其实需要不同的回收策略才能达到最好的效果,所以V8采用分代式垃圾回收机制,降对象按的存活时间进行不同的分代,然后对不同分代(新生代、老生代)的内存施以更适合也更好的算法。
新生代中的对象存活时间较短,而老生代中的对象存活时间较长甚至是常驻内存。基于此所以设计的新生代的内存普遍要比老生代的内存小很多,V8中新生代最大内存是32M(64位系统为例),老生代最大内存是1400MB。V8实际使用的堆内存大小是新生代+老生代所用内存之和(1432MB),但是V8最大值其实是比使用的内存对大小额外大了32M(1464MB)

新生代如何做垃圾回收?

新生代的采用名叫Scavenge的垃圾回收算法。在Scavenge的具体实现中,主要采用了Cheney算法,Cheney算法通过将新生代堆一分为二,一个使用(From semispace),一个空闲(To semispace)。创建对象的时候,现在From空间中进行分配,当需要进行垃圾回收时,就检查From空间中的存活对象,然后将存活的对象拷贝到To空间,同时清空From空间,并将From和To互换,整个垃圾回收过程中就是将存活对象在两个seispace之间进行复制。对于生命周期短的场景存活对象在整个对象中占比较小,所以Scavenge采用的是复制存活的对象,但是Scavenge只能利用堆内存一半的空间,这是典型的用空间换时间的体现。
当一个对象经过多次垃圾回收(一般两次)依然存活的话,就会被认为是生命周期较长的对象,一方面新生代堆比较小,另一方面重复复制生命周期长的对象也很没有效率,所以对于生命周期长的对象会被移到老生代中去。新生代对象移动到老生代有两个对象:1.对象是否是生命周期较长的对象(已经经历过垃圾回收)2.To空间使用占比是否超过了25%。限制25%的原因是由于垃圾回收完成后To会变成From,如果不做限制的话可能会出现From很快被用光的情况,出现频繁的垃圾回收,也会影响效率。

老生代如何做垃圾回收?

老生代由于存活对象占较大比重,不适合对存活对象进行操作,使用Scavenge算法就不太合适了,因此老生代采用了Mark-Sweep和Mark-Compact相结合的方式。
Mark-Sweep分为标记和清除两个阶段,在标记阶段遍历堆中所有对象,标记活着的对象,然后在清除阶段未被标记的对象将会被清除掉。Mark-Sweep解决了内存释放的问题但是由于没有像Scavenge那样复制对象的操作导致内存碎片化不连续。而Mark-Compact就是用来解决内存碎片化问题的。Mark-Compact会将存活的对象往一端移动,移动完成后直接清理掉边界外的内存,这样就有大段的连续可用内存了,但是由于涉及到对象的移动,因此Mark-Compact的速度要比Mark-Sweep慢了。V8主要使用Mark-Sweep,只有当空间不足以对新生代中今生过来的对象进行分配时才使用Mark-Compact。

垃圾回收过程中会导致应用程序暂停执行,由于新生代本身空间较小,且要复制的存活对象占比也少,因此即便执行全量垃圾回收也影响不大,但是老生代空间很大,存活对象也多,执行一次全量垃圾回收对于应用程序暂停会是一个比较长的时间,因此V8将老生的标记改成了增量更新的方式,使得标记和应用程序交替执行直到标记完成,然后垃圾回收再执行后面的清理工作。注意清理工作并不是增量的。

开发者可以指定强制垃圾回收吗?

答案是可以了,在启动node服务的时候使用--expose-gc flag

$ node --expose-gc file.js

这样全局对象上就有了执行垃圾回收的函数

global.gc();

推荐更安全的写法

function forceGC()if (global.gc) {global.gc();} else {console.warn('No GC hook! Start your program as `node --expose-gc file.js`.');}
}
参考资料

https://speakerdeck.com/addyo...
http://newhtml.net/v8-garbage...
https://www.xarg.org/2016/06/...
https://v8project.blogspot.co...
https://v8project.blogspot.de...
https://v8project.blogspot.it...
http://alinode.aliyun.com/blo...

Nodejs内存治理相关推荐

  1. 腾讯音乐MOO音乐应用的Flutter内存治理实战分享

    MOO音乐是 TME 旗下的新锐音乐服务,其团队是公司内最早实践 Flutter 的先行者之一.本系列文章将提炼 MOO APP 开发中遇到的情况,就 Flutter 内存占用治理方面,分享日常开发的 ...

  2. nodejs内存溢出解决方法

    nodejs内存溢出解决方法 参考文章: (1)nodejs内存溢出解决方法 (2)https://www.cnblogs.com/qiyc/p/9634483.html 备忘一下.

  3. 搞不定 NodeJS 内存泄漏?先从了解垃圾回收开始

    通常来说,内存管理有两种方式,一种是手动管理,一种是自动管理. 手动管理需要开发者自己管理内存,什么时候申请内存空间,什么时候释放都需要小心处理,否则容易形成内存泄漏和指针乱飞的局面.C 语言开发是典 ...

  4. Nodejs内存溢出原因

    内存溢出的原因 NodeJS 程序出现内存溢出问题,可以分为三个方面: V8内存限制 使用不规范 (闭包,对象内存及时未释放) 解决方案 使用 async/await 对于第一种异常情况,可以通过 a ...

  5. NodeJS内存溢出解决办法

    FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - nodeJs打包时内存溢出 打开一个cmd窗口 运 ...

  6. nodejs内存溢出

    npm-v 报错,错误信息如下: FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScri ...

  7. Nodejs内存控制详解(上篇)

    在这里输入图片标题 1 V8的垃圾回收机制与内存限制 JavaScript与Java一样,由垃圾回收机制来进行自动的内存管理.对于性能敏感的服务器端程序,内存管理的好坏.垃圾回收状况是否优良,都会对服 ...

  8. python做内存辅助_pubg辅助_Python一切皆是工具,但这和内存治理有什么关系?

    今天是Python的第15篇文章,我们来聊聊Python中内存管理机制,以及循环引用的问题. Python的内存管理机制 对于工程师而言,内存管理机制异常主要,是绕不外去的一环.若是你是Java工程师 ...

  9. win10下nodejs内存泄漏查找工具node-heapdump和node-memwatch的安装

    最近在看<深入浅出Node.js>,看到了第5章提到的node-heapdump和node-memwatch内在泄漏查找工具,我就尝试在win10下安装和使用一下,觉得有必要总结一下. n ...

最新文章

  1. Tensorflow中的mnist例子
  2. c++ 异步回调_知道Java中的回调机制吗?
  3. 文件系统应用笔记之一:FatFS在STM32F4上的移植
  4. 浅谈asio中async_accept函数占用内存高的写法
  5. redis-shake数据同步迁移工具
  6. Windows笔记本网卡驱动坏了/wifi图标消失
  7. 【晶体管电路设计】一、晶体管设计概述与共射极放大电路
  8. 插入导频法实现载波通信matlab,插入导频法.ppt
  9. KEGG 下载 Pathway 通路数据
  10. 关于IPB帧与恒定比特率、动态比特率的详解
  11. awtk + scons资源/问题/调试
  12. 关系型数据库第四章笔记---关系数据理论
  13. [C++]snprintf函数
  14. 论苹果与安卓系统争霸
  15. 0到9的数字替换成零 到 玖 的 大写汉字的函数
  16. matlab 画图 断层显示,MATLAB编程实现连续断层工业CT图像的三维重建_张爱东
  17. PID控制 优选法(黄金分割法)具体试验方法
  18. IT人如何开始自己创业(轉)
  19. 扔鸡蛋问题-方程-动态规划
  20. 网页颜色搭配技巧nbsp;文字字体、字号…

热门文章

  1. matlab里的pid参数调节,PID控制参数整定(调节方法)原理+图示+MATLAB调试
  2. python ocr 识别车牌_python+opencv+TESSERT-OCR实现车牌的检测与识别
  3. 数通工程师中,最多人拿10K-15K的工资,偷学他们是怎么发展的?
  4. 【转】VTK和ITK中的坐标系统
  5. 首版次高端软件申报条件和好处
  6. Hyper-V的CentOS设置固定IP
  7. 土壤科学系的李博士,你还好么
  8. mac android 打包,android studio如何生成apk android studio打包apk教程
  9. We have the power to change the world.
  10. [附源码]Nodejs计算机毕业设计基于框架的动漫设计Express(程序+LW)