重学Node系列03-内存管理及GC算法

简介: Node内存控制这是重新阅读《深入浅出NodeJS》的相关笔记,这次阅读发现自己依旧收获很多,而第一次阅读的东西也差不多忘记完了,所以想着这次过一遍脑子,用自己的理解输出一下,方便记忆以及以后回忆...

Node内存控制

这是重新阅读《深入浅出NodeJS》的相关笔记,这次阅读发现自己依旧收获很多,而第一次阅读的东西也差不多忘记完了,所以想着这次过一遍脑子,用自己的理解输出一下,方便记忆以及以后回忆...

基本介绍


说到node对于内存的控制,可能最先想到的就是node是基于V8构建,因此在node中通过JavaScript使用内存时就会发现只能使用部分内存(64位系统约为1.4GB,32位系统约为0.7GB)。在这样的限制下,将会导致node无法直接操作大内存对象,即使你本机的物理内存有32GB也不行,这与我们传统上的认知形成了一定的差别,接下来先解释一下为什么有这样的差别:

注:64位系统约为1.4GB,32位系统约为0.7GB为默认,也可以用户自定义--max-old-space-size--max-new-space-size来调整,不过只能在启动时指定,node无法运行中自动扩充

首先,V8的设计就是在浏览器的应用场景下完成的,这套内存管理机制在浏览器下使用是绰绰有余的,只是在node中使用有些限制,当然,也有其他方式来解决,只是不能让开发者随心所欲地使用大内存了。

其实深层原因是V8的垃圾回收机制的限制,每次垃圾回收都必须让JavaScript线程暂停,如果垃圾回收时间过长会导致应用的性能和响应能力都会直线下降,所以V8当时的考虑直接限制堆内存是一个好的选择;这中停顿叫做“全停顿”,V8为此也还做了许多优化,这个后续章节会讲到

回到这一部分,介绍一个比较常见的命令:我们在node中输入process.memoryUsage(),就可以很方便地查看node内存使用量的相关信息:

简单解释一下其中的含义,详细参考

注:V8中的所有JavaScript对象都是通过堆来进行分配的

  • rss:Resident Set Size,是该进程所占用的总空间量,包含所有C++JavaScript对象和代码
  • heapTotal:已经申请到的堆内存
  • heapUsed:当前使用的堆内存
  • external:指绑定到 V8 管理的 JavaScript 对象的 C++ 对象的内存使用情况
  • arrayBuffers:包含的Buffer对象,该值包含在external

值得注意的是:V8在上述变量中只负责了堆内存的分配,external包含的内存并不是通过V8管理的,所以我们在external中操作的东西可以不受V8的内存限

垃圾回收机制


总的来说,V8的垃圾回收策略是基于分代式垃圾回收机制,分为新生代和老生代。其中新生代的对象为存活时间较短的对象,老生代的对象为存活时间较长的对象,这里先介绍结论,具体原因后续讲到。

新生代


新生代中主要通过Scavenge算法进行垃圾回收,这是典型的空间换时间的算法,会牺牲一半的存储空间,但速度较快,正好与新生代中对象的特点相对应

该算法主要采取复制的方式进行的垃圾回收,如下图所示:

之前提到过这个算法与新生代的特点向符合,解释一下,新生代里面对象的特点就是生命周期较短,所以在下一次复制过程中存活的对象一般来说是比较少的,而这个算法是只复制存活的对象,所以时间效率上有优异的表现,同时这中将存活中的对象直接在另一半内存空间中依次排列,不会产生老生代那种算法的内存碎片问题(后续会讲到)

晋升

其实一开始对象声明的时候,V8也不知道这个对象是否生命周期较短,那它是如何判断从而将对象区分到新生代区域和老生代区域之中的呢?

答案就是本小节的标题,“晋升”:当一个对象经过多次复制依然存活时或者该对象To空间的占用比超出限制(一般25%),它将会被执行晋升操作,放入到老生代区域之中(注意:只要这两个条件满足一个,就会执行晋升操作,这是个“或”条件)

老生代


结合老生代的特点,V8在老生代中主要采取了Mark-SweepMark-Compact相结合的方式进行垃圾回收,就是标记清除和标记整理

标记清除

  • 标记阶段:标记活着的对象
  • 清除阶段:清除没被标记的对象

老生代的特点就是存活时间长,即失活对象的占比一般来说是比较小的,所以这里是清除死亡的对象是合理的。而不是与新生代算法一致,复制活着的对象,并且由于老生代对象占用内存较大,所以分出一半空间来说也是不合理的

标记整理:在标记清除的基础上提出来的,在对象标记为死亡后,整理的过程中,会将活着的对象往一边移动,移动完成后,直接清理掉边界外的内存。

注意:V8并不是直接采取标记整理的方式来管理老生代,而是通过标记清除和标记整理相结合的方式进行处理,因为它们在处理效率上有较大差别,毕竟标记清理多做了移动的操作。

回收算法 Mark-Sweep Mark-compact Scavenge
速度 中等 最慢 最快
空间开销 少(有碎片) 少(无碎片) 双倍空间(无碎片)
是否移动对象

这种分层级处理方式在计算机中非常常见,最容易想到的就是比如速度上寄存器 > 内存 > 外存,而价格上寄存器 < 内存 < 外存,所以计算机的存储结构就采取了三级分层策略来平衡速度与价格;就像V8中于内存的处理是在时间和空间以及内存碎片等维度上进行平衡的。

所以在取舍中,V8主要使用标记清除算法,在空间不足以对新生代晋升过来的对象进行分配的时候才使用标记整理算法(如下图)

全停顿问题


上述中的三种基本垃圾回收算法都需要将应用逻辑(JavaScript执行线程)暂停下来,待执行完垃圾回收后再恢复执行应用逻辑,这样做是为了避免JavaScript应用逻辑与垃圾回收器看到的不一致的情况。这种行为就是文章前面提到的“全停顿”;

而且老生代通常配置得较大,且活动对象较多,全堆垃圾回收得标记、清理、整理等动作造成得停顿就会比较可怕,需要优化:

这就是“增量标记”出现的原因,具体过程就是将原本要以口气停顿完成的动作改为增量标记的方式,也就是拆分为寻多个小“步进”,每做完一个“步进”就让JavaScript执行一小会儿

同时还有延迟清理与增量式整理,让清理和整理的动作也变成增量式等一系列优化操作,这里不深入研究。

内存泄漏


通常造成内存泄漏的原因有这些:

  • 缓存:把内存作缓存,却没有过期策略清除,导致越来越多
  • 队列消费不及时:生产速度远远大于消费速度,队列长度没做限制的话就会无限变大,导致内存泄漏
  • 作用域未释放

内存监控及内存泄漏解决方案


TODO,功力不够,后续来补,主要是还没实践经验,这里先挖个坑。。。



目录
相关文章
|
1月前
|
监控 JavaScript 算法
如何使用内存监控工具来定位和解决Node.js应用中的性能问题?
总之,利用内存监控工具结合代码分析和业务理解,能够逐步定位和解决 Node.js 应用中的性能问题,提高应用的运行效率和稳定性。需要耐心和细致地进行排查和优化,不断提升应用的性能表现。
181 77
|
1月前
|
监控 JavaScript
选择适合自己的Node.js内存监控工具
选择合适的内存监控工具是优化 Node.js 应用内存使用的重要一步,它可以帮助你更好地了解内存状况,及时发现问题并采取措施,提高应用的性能和稳定性。
117 76
|
1月前
|
监控 JavaScript 数据库连接
解读Node.js内存监控工具生成的报告
需要注意的是,不同的内存监控工具可能会有不同的报告格式和内容,具体的解读方法可能会有所差异。因此,在使用具体工具时,还需要参考其相关的文档和说明,以更好地理解和利用报告中的信息。通过深入解读内存监控报告,我们可以不断优化 Node.js 应用的内存使用,提高其性能和稳定性。
103 74
|
1月前
|
Web App开发 缓存 监控
如何解决Node框架中内存管理的挑战?
解决 Node 框架中内存管理的挑战需要综合运用多种方法,并且需要在开发过程中保持谨慎和细心,不断优化和改进代码。同时,定期进行内存管理的检查和维护也是非常重要的。
114 63
|
1月前
|
存储 缓存 JavaScript
如何优化Node.js应用的内存使用以提高性能?
通过以上多种方法的综合运用,可以有效地优化 Node.js 应用的内存使用,提高性能,提升用户体验。同时,不断关注内存管理的最新技术和最佳实践,持续改进应用的性能表现。
124 62
|
1月前
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
135 52
|
1月前
|
存储 缓存 监控
如何使用内存监控工具来优化 Node.js 应用的性能
需要注意的是,不同的内存监控工具可能具有不同的功能和特点,在使用时需要根据具体工具的要求和操作指南进行正确使用和分析。
70 31
|
24天前
|
机器学习/深度学习 人工智能 算法
【AI系统】内存分配算法
本文探讨了AI编译器前端优化中的内存分配问题,涵盖模型与硬件内存的发展、内存划分及其优化算法。文章首先分析了神经网络模型对NPU内存需求的增长趋势,随后详细介绍了静态与动态内存的概念及其实现方式,最后重点讨论了几种节省内存的算法,如空间换内存、计算换内存、模型压缩和内存复用等,旨在提高内存使用效率,减少碎片化,提升模型训练和推理的性能。
44 1
|
28天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
62 1
|
1月前
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
44 3

热门文章

最新文章