探索一下Node中的堆内存分配,聊聊内存限制!

本篇文章带大家探索一下node中的堆内存分配,深入了解一下node.js中的内存限制,希望对大家有所帮助!

探索一下Node中的堆内存分配,聊聊内存限制!

在本篇文章中,我将探索一下Node中的堆内存分配,然后试试看把内存提高到硬件能承受的极限。然后我们将找到一些实用的方法来监控 Node 的进程以调试内存相关问题。

OK,准备完成就发车!

大家可以在仓库拉一下相关代码 clone the code from my GitHub:https://github.com/beautifulcoder/node-memory-limitations

V8 垃圾回收简介

首先,简单介绍一下V8垃圾回收器。内存的存储分配方式是堆(heap),堆被分为几个世代(generational)区域。对象在它的生命周期中随着年龄的变化,它所属的世代也有所不同。

世代中分为年轻一代和老一代,而年轻的一代还分为了新生代和中间代。随着对象在垃圾回收中幸存下来,它们也会加入老一代。

1.png

世代假说的基本原则是大多数对象都是年轻的。V8 垃圾回收器基于这一点,只提升在垃圾回收中幸存下来的对象。随着对象被复制到相邻区域,它们最终会进入老一代。

在Nodejs中内存消耗主要分为三个方面:

代码-代码执行时所在的位置调用栈-用于存放具有原始类型(数字,字符串或布尔值)的函数和局部变量堆内存

堆内存是我们今天的主要关注点。现在您对垃圾回收器有了更多的了解,是时候在堆上分配一些内存了!

function allocateMemory(size) {  // Simulate allocation of bytes  const numbers = size / 8;  const arr = [];  arr.length = numbers;  for (let i = 0; i < numbers; i++) {    arr[i] = i;  }  return arr;}

在调用栈中,局部变量随着函数调用结束而销毁。基础类型 number永远不会进入堆内存,而是在调用栈中分配。但是对象arr将进入堆中并且可能在垃圾回收中幸存下来。

堆内存有限制吗?

现在进行勇敢测试——将 Node 进程推到极限看看在哪个地方会耗尽堆内存:

const memoryLeakAllocations = [];const field = "heapUsed";const allocationStep = 10000 * 1024; // 10MBconst TIME_INTERVAL_IN_MSEC = 40;setInterval(() => {  const allocation = allocateMemory(allocationStep);  memoryLeakAllocations.push(allocation);  const mu = process.memoryUsage();  // # bytes / KB / MB / GB  const gbNow = mu[field] / 1024 / 1024 / 1024;  const gbRounded = Math.round(gbNow * 100) / 100;  console.log(`Heap allocated ${gbRounded} GB`);}, TIME_INTERVAL_IN_MSEC);

在上面的代码中,我们以 40 毫秒的间隔分配了大约 10 mb,为垃圾回收提供了足够的时间来将幸存的对象提升到老年代。process.memoryUsage 是一个用于回收有关堆利用率的粗略指标的工具。随着堆分配的增长,heapUsed 字段会记录堆的大小。这个字段记录 RAM 中的字节数,可以转换为mb。

你的结果可能会有所不同。在32GB 内存的 Windows 10 笔记本电脑会得到以下结果:

Heap allocated 4 GBHeap allocated 4.01 GB[18820:000001A45B4680A0] 26146 ms: Mark-sweep (reduce) 4103.7 (4107.3) -> 4103.7 (4108.3) MB, 1196.5 / 0.0 ms (average mu = 0.112, current mu = 0.000) last resort GC in old space requestedFATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

在这里,垃圾回收器将尝试压缩内存作为最后的手段,最后放弃并抛出“堆内存不足”异常。这个过程达到了 4.1GB 的限制,需要 26.6 秒才能意识到要把服务给挂掉了。

导致以上结果的原因有些还未知。V8 垃圾回收器最初运行在具有严格内存限制的 32 位浏览器进程中。这些结果表明内存限制可能已经从遗留代码中继承下来。

在撰写本文时,以上代码在最新的 LTS Node 版本下运行,并且使用的是 64 位可执行文件。从理论上讲,一个 64 位进程应该能够分配超过 4GB 的空间,并且可以轻松地增长到 16 TB 的地址空间。

扩大内存分配限制

node index.js --max-old-space-size=8000

这将最大限制设置为 8GB。这样做时要小心。我的笔记本电脑有 32GB的空间。我建议将其设置为 RAM 中实际可用的空间。一旦物理内存耗尽,进程就会开始通过虚拟内存占用磁盘空间。如果您将限制设置得太高,你就get了换电脑的新理由,这里咱们尽量避免电脑冒烟了哈~

我们再用8GB的限制再跑一次代码:

Heap allocated 7.8 GBHeap allocated 7.81 GB[16976:000001ACB8FEB330] 45701 ms: Mark-sweep (reduce) 8000.2 (8005.3) -> 8000.2 (8006.3) MB, 1468.4 / 0.0 ms (average mu = 0.211, current mu = 0.000) last resort GC in old space requestedFATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

这一次堆的大小几乎达到 8GB,但没完全达到。我怀疑是Node 进程中有一些开销用于分配这么多内存。这次进程结束需要 45.7 秒。

在生产环境中,内存全部用完可能不会少于一分钟。这就是监控和洞察内存消耗有帮助的原因之一。内存消耗会随着时间的推移缓慢增长,并且可能需要几天时间才能知道存在问题。如果进程不断崩溃并且日志中出现“堆内存不足”异常,则代码中可能存在内存泄漏。

进程也可能会占用更多内存,因为它正在处理更多数据。如果资源消耗继续增长,可能是时候将这个单体分解为微服务了。这将减少单个进程的内存压力,并允许节点水平扩展。

如何跟踪 Node.js 内存泄漏

process.memoryUsage 的 heapUsed  字段还是有点用的,调试内存泄漏的一个方法是将内存指标放在另一个工具中以进行进一步处理。由于此实现并不复杂,因此主要解析下如何亲自实现。

const path = require("path");const fs = require("fs");const os = require("os");const start = Date.now();const LOG_FILE = path.join(__dirname, "memory-usage.csv");fs.writeFile(LOG_FILE, "Time Alive (secs),Memory GB" + os.EOL, () => {}); // 请求-确认

为了避免将堆分配指标放在内存中,我们选择将结果写入 CSV 文件以方便数据消耗。这里使用了 writeFile 带有回调的异步函数。回调为空以写入文件并继续,无需任何进一步处理。要获取渐进式内存指标,请将其添加到 console.log:

const elapsedTimeInSecs = (Date.now() - start) / 1000;const timeRounded = Math.round(elapsedTimeInSecs * 100) / 100;s.appendFile(LOG_FILE, timeRounded + "," + gbRounded + os.EOL, () => {}); // 请求-确认

上面这段代码可以用来调试内存泄漏的情况下,堆内存随着时间变化而增长。你可以使用一些分析工具来解析原生csv数据以实现一个比较漂亮的可视化。

存了个图 存了个图

视频图片解析/字幕/剪辑,视频高清保存/图片源图提取

存了个图 17 查看详情 存了个图

如果你只是赶着看看数据的情况,直接用excel也可以,如下图:

2.png

在限制为4.1GB的情况下,你可以看到内存的使用率在短时间内呈线性增长。内存的消耗在持续的增长并没有变得平缓,这个说明了某个地方存在内存泄漏。在我们调试这类问题的时候,我们要寻找在分配在老世代结束时的那部分代码。

对象如果再在垃圾回收时幸存下来,就可能会一直存在,直到进程终止。

使用这段内存泄漏检测代码更具复用性的一种方法是将其包装在自己的时间间隔内(因为它不必存在于主循环中)。

setInterval(() => {  const mu = process.memoryUsage();  // # bytes / KB / MB / GB  const gbNow = mu[field] / 1024 / 1024 / 1024;  const gbRounded = Math.round(gbNow * 100) / 100;  const elapsedTimeInSecs = (Date.now() - start) / 1000;  const timeRounded = Math.round(elapsedTimeInSecs * 100) / 100;  fs.appendFile(LOG_FILE, timeRounded + "," + gbRounded + os.EOL, () => {}); // fire-and-forget}, TIME_INTERVAL_IN_MSEC);

要注意上面这些方法并不能直接在生产环境中使用,仅仅只是告诉你如何在本地环境调试内存泄漏。在实际实现时还包括了自动显示、警报和轮换日志,这样服务器才不会耗尽磁盘空间。

跟踪生产环境中的 Node.js 内存泄漏

尽管上面的代码在生产环境中不可行,但我们已经看到了如何去调试内存泄漏。因此,作为替代方案,可以将 Node 进程包裹在 PM2 之类 的 守护进程 中。

当内存消耗达到限制时设置重启策略:

pm2 start index.js --max-memory-restart 8G

单位可以是 K(千字节)、M(兆字节)和 G(千兆字节)。进程重启大约需要 30 秒,因此通过负载均衡器配置多个节点以避免中断。

另一个漂亮的工具是跨平台的原生模块node-memwatch,它在检测到运行代码中的内存泄漏时触发一个事件。

const memwatch = require("memwatch");memwatch.on("leak", function (info) {  // event emitted  console.log(info.reason);});复制代码

事件通过leak触发,并且它的回调对象中有一个reason会随着连续垃圾回收的堆增长而增长。

使用 AppSignal 的 Magic Dashboard 诊断内存限制

AppSignal 有一个神奇的仪表板,用于监控堆增长的垃圾收集统计信息。

3.png

上图显示请求在 14:25 左右停止了 7 分钟,允许垃圾回收以减少内存压力。当对象在旧的空间中停留太久并导致内存泄漏时,仪表板也会暴露出来。

总结:解决 Node.js 内存限制和泄漏

在这篇文章中,我们首先了解了 V8 垃圾回收器的作用,然后再探讨堆内存是否存在限制以及如何扩展内存分配限制。

最后,我们使用了一些潜在的工具来密切关注 Node.js 中的内存泄漏。我们看到内存分配的监控可以通过使用一些粗略的工具方法来实现,比如memoryUsage一些调试方法。在这里,分析仍然是手动实现的。

另一种选择是使用 AppSignal 等专业工具,它提供监控、警报和漂亮的可视化来实时诊断内存问题。

希望你喜欢这篇关于内存限制和诊断内存泄漏的快速介绍。

更多node相关知识,请访问:nodejs 教程!!

以上就是探索一下Node中的堆内存分配,聊聊内存限制!的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/553449.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Linux backlog产生原因分析
上一篇 2025年11月9日 21:13:10
《阿里巴巴》绑定支付宝教程
下一篇 2025年11月9日 21:13:15

相关推荐

  • JavaScript 高效判断页面所有复选框状态的技巧与实践

    本文旨在提供一套高效且专业的javascript方法,用于判断网页中所有复选框的选中状态。我们将探讨如何利用`array.some()`快速确定是否有未选中的复选框(进而判断是否全部选中),以及如何使用`array.filter()`统计选中和未选中的复选框数量。通过优化dom元素选择和数组操作,提…

    2026年5月10日
    000
  • OSMnx中interpolate_points函数详解及街道细分与图构建实践

    本文详细介绍了osmnx库中`utils_geo.interpolate_points`函数的使用方法,特别是其返回的python生成器类型。我们将学习如何处理生成器输出,并提供一个完整的教程,演示如何利用此函数将现有街道几何体细分为更小的线段,进而构建一个精细化的网络图,以支持更细粒度的空间分析。…

    2026年5月10日
    000
  • 如何安全有效地从外部网页获取HTML元素数据并应用于自身页面

    本教程旨在解决如何在不同域名下,通过javascript获取并使用另一个网页的html元素数据。文章将深入探讨同源策略的限制,并提供两种主要解决方案:使用` 在现代Web开发中,有时我们需要从外部网站获取特定的HTML内容或属性值,并将其整合到我们自己的网页中。例如,从XYZ.COM/B.html页…

    2026年5月10日
    000
  • JS如何操作HTML元素_DOM编程核心方法【教程】

    必须掌握操作HTML元素的核心DOM方法:一、通过ID获取单个元素;二、通过类名获取元素集合;三、通过标签名获取元素集合;四、通过CSS选择器获取元素;五、为元素绑定事件监听器;六、创建并插入新元素;七、替换或删除现有元素。 如果您希望使用JavaScript动态修改网页内容、响应用户交互或构建交互…

    2026年5月10日
    000
  • Golang如何提升TCP长连接处理效率_Golang TCP长连接处理性能优化实践详解

    答案:通过非阻塞I/O、单Goroutine双工模型、sync.Pool对象复用、TCP_NODELAY优化及高效心跳管理,结合系统调优,可显著提升Golang百万级TCP长连接处理效率。 在高并发网络服务场景中,TCP长连接的处理效率直接影响系统的吞吐能力和资源消耗。Golang凭借其轻量级Gor…

    2026年5月10日
    000
  • 使用SMTP.js发送邮件:客户端集成、常见问题与最佳实践指南

    本文深入探讨了使用SMTP.js库在前端发送邮件时可能遇到的问题,特别是与Elastic Email集成时的挑战。我们将分析代码中常见的异步处理错误、条件函数定义陷阱,并提供修正后的代码示例和最佳实践。重点强调了正确处理Promise链、确保函数可访问性以及客户端邮件发送的安全考量,帮助开发者构建更…

    2026年5月10日
    000
  • 如何在不暴露密钥的情况下,在客户端创建 Stripe Payment Link

    本文介绍了在纯静态网站环境下,如何利用 Stripe Payment Link 实现商品售卖,并着重讨论了在不暴露 Stripe 密钥的前提下,客户端创建 Payment Link 的可行性。分析了直接在客户端使用密钥的风险,并提出了预先生成 Payment Link 或使用后端服务动态生成 Pay…

    2026年5月10日
    000
  • Windows用Prettier一键格式化乱码HTML代码

    首先确保HTML文件保存为UTF-8编码,使用文本编辑器另存为UTF-8格式;其次在命令行执行chcp 65001切换至UTF-8代码页后再运行Prettier;接着在VS Code中设置files.encoding为utf8并启用files.autoGuessEncoding;最后可通过Node.…

    2026年5月10日
    000
  • php怎么截取网页_php抓取网页内容的几种方法

    file_get_contents适用于静态页抓取,但受限于allow_url_fopen且无法执行JS;2. cURL支持自定义请求头、Cookie等,适合处理复杂HTTP请求;3. Guzzle作为现代PHP项目推荐方案,具备良好扩展性与异步支持;4. 动态渲染内容需借助Puppeteer或Se…

    2026年5月10日
    000
  • html函数如何实现动态内容显示 html函数在网页交互中的核心应用

    JavaScript函数通过操作DOM实现动态内容更新与交互,如显示时间、实时搜索、增删元素及加载数据,使网页具备动态功能。 HTML 本身没有“函数”的概念,它是一种标记语言,用于定义网页结构。真正实现动态内容显示和交互功能的是 JavaScript。通常所说的“HTML函数”其实是 JavaSc…

    2026年5月10日
    000
  • HTMLAMP怎么做_加速移动页面实现教程

    答案:HTML AMP通过规范标签、禁用自定义JS、引入AMP JS库和缓存技术提升移动页面加载速度,需遵循AMP HTML标准并验证有效性,有助于SEO但非万能,未来将更开放并与PWA等融合。 HTML AMP 旨在加速移动页面加载速度,提升用户体验。简单来说,它通过限制某些 HTML 功能,并采…

    2026年5月10日
    000
  • 理解PHP服务器端请求与浏览器开发者工具的限制

    当PHP脚本使用file_get_contents等函数发起服务器端请求时,这些请求直接在服务器上执行,而非通过浏览器。因此,浏览器开发者工具的网络活动面板无法捕获和显示这些内部的服务器间通信,因为它仅监控浏览器自身发出的网络请求,对服务器内部处理过程无感知。 客户端请求与服务器端请求的本质区别 在…

    2026年5月10日
    000
  • JavaScript DOM操作:点击关联元素获取目标文本内容的教程

    本教程详细介绍了如何通过JavaScript处理用户点击事件,并结合DOM的 closest() 和 querySelector() 方法,从复杂的HTML结构中准确获取目标元素的文本内容。文章强调了使用 addEventListener() 进行事件绑定、避免重复ID以及高效DOM遍历的最佳实践,…

    2026年5月10日
    000
  • 前端构建优化:利用常量折叠提升应用性能

    本文深入探讨了一种在构建阶段执行部分源代码以进行优化的技术——常量折叠(Constant Folding)。通过在编译时预计算表达式并替换为最终结果,该技术显著减少了运行时开销,提升了应用性能。文章将详细解释其工作原理、优势,并探讨其在现代前端构建工具中的应用与配置,旨在帮助开发者实现更高效的代码优…

    2026年5月10日
    000
  • JavaScript数据结构实现_javascript算法基础

    JavaScript中常用数据结构包括栈、链表和字典:1. 栈利用数组的push和pop实现LIFO,适用于括号匹配;2. 链表由节点组成,插入删除高效,适合频繁修改场景;3. 字典用对象实现键值对存储,常用于频率统计;4. 二分查找在有序数组中以O(log n)效率查找目标值,需数组已排序。掌握这…

    2026年5月10日
    000
  • JavaScript井字棋(Tic-Tac-Toe)核心交互逻辑实现教程

    本教程详细介绍了如何使用javascript实现井字棋(tic-tac-toe)游戏的核心交互逻辑。内容涵盖了如何遍历并为棋盘上的每个方格添加点击事件监听器,实现玩家x和o的交替落子,以及重置游戏状态的功能。通过提供的html、css和javascript代码示例,读者可以快速理解并构建一个基础的井…

    2026年5月10日
    000
  • 全栈JS代码怎么结构化_全栈JavaScript项目代码结构与规范指南

    采用分层+功能划分的目录结构,明确分离前后端代码;2. 遵循单一职责原则,路由、控制器、服务与模型各司其职;3. 统一命名规范并集成ESLint+Prettier保证代码风格一致;4. 使用环境变量管理配置,通过脚本实现自动化构建与并发启动服务。 全栈JavaScript项目涉及前端、后端、数据库交…

    2026年5月10日
    000
  • JavaScript模块加载机制_JavaScript代码组织规范

    现代前端推荐使用ES Modules,通过import和export实现静态依赖管理,配合合理目录结构与命名规范提升可维护性,注意浏览器与Node.js的运行差异。 JavaScript 的模块加载机制和代码组织规范是现代前端开发中的核心基础。随着项目规模扩大,良好的模块化设计能提升代码可维护性、复…

    2026年5月10日
    000
  • c++怎么实现一个静态代码分析工具_C++代码质量与静态分析工具开发

    静态代码分析工具通过解析源码构建AST,利用Clang框架实现未使用变量检测,结合ASTMatchers进行规则匹配,最终生成警告信息。 静态代码分析工具可以在不运行程序的前提下,检测出潜在的语法错误、编码规范问题、内存泄漏风险等。在C++中开发一个简单的静态分析工具,核心思路是解析源码并构建抽象语…

    2026年5月10日
    000
  • JavaScript模块化是什么_ES6模块和CommonJS有什么区别呢

    JavaScript模块化将代码拆分为独立可复用单元,ES6模块(import/export,编译时加载、实时引用)与CommonJS(require/module.exports,运行时加载、值拷贝)核心区别在于设计目标和运行机制不同。 JavaScript模块化是把代码按功能拆分成独立、可复用的…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信