Node.js 堆内存不足

Node.js heap out of memory

今天我 运行 我的文件系统索引脚本刷新 RAID 文件索引,4 小时后它崩溃并出现以下错误:

[md5:]  241613/241627 97.5%  
[md5:]  241614/241627 97.5%  
[md5:]  241625/241627 98.1%
Creating missing list... (79570 files missing)
Creating new files list... (241627 new files)

<--- Last few GCs --->

11629672 ms: Mark-sweep 1174.6 (1426.5) -> 1172.4 (1418.3) MB, 659.9 / 0 ms [allocation failure] [GC in old space requested].
11630371 ms: Mark-sweep 1172.4 (1418.3) -> 1172.4 (1411.3) MB, 698.9 / 0 ms [allocation failure] [GC in old space requested].
11631105 ms: Mark-sweep 1172.4 (1411.3) -> 1172.4 (1389.3) MB, 733.5 / 0 ms [last resort gc].
11631778 ms: Mark-sweep 1172.4 (1389.3) -> 1172.4 (1368.3) MB, 673.6 / 0 ms [last resort gc].


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x3d1d329c9e59 <JS Object>
1: SparseJoinWithSeparatorJS(aka SparseJoinWithSeparatorJS) [native array.js:~84] [pc=0x3629ef689ad0] (this=0x3d1d32904189 <undefined>,w=0x2b690ce91071 <JS Array[241627]>,L=241627,M=0x3d1d329b4a11 <JS Function ConvertToString (SharedFunctionInfo 0x3d1d3294ef79)>,N=0x7c953bf4d49 <String[4]\: ,\n  >)
2: Join(aka Join) [native array.js:143] [pc=0x3629ef616696] (this=0x3d1d32904189 <undefin...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node::Abort() [/usr/bin/node]
 2: 0xe2c5fc [/usr/bin/node]
 3: v8::Utils::ReportApiFailure(char const*, char const*) [/usr/bin/node]
 4: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [/usr/bin/node]
 5: v8::internal::Factory::NewRawTwoByteString(int, v8::internal::PretenureFlag) [/usr/bin/node]
 6: v8::internal::Runtime_SparseJoinWithSeparator(int, v8::internal::Object**, v8::internal::Isolate*) [/usr/bin/node]
 7: 0x3629ef50961b

服务器配备 16gb RAM 和 24gb SSD swap。我非常怀疑我的脚本超过了 36gb 的内存。至少不应该

脚本使用文件元数据(修改日期、权限等,无大数据)创建存储为对象数组的文件索引

完整脚本代码如下: http://pastebin.com/mjaD76c3

我过去已经用这个脚本遇到过奇怪的节点问题是什么迫使我,例如。将索引拆分为多个文件,因为节点在处理像 String 这样的大文件时出现故障。有没有什么方法可以改进 nodejs 对巨大数据集的内存管理?

如果我没记错的话,V8 中的内存使用有严格的标准限制,如果您不手动增加内存使用量,大约为 1.7 GB。

在我们的一个产品中,我们在部署脚本中遵循了这个解决方案:

 node --max-old-space-size=4096 yourFile.js

还会有一个新的 space 命令,但正如我在这里读到的:a-tour-of-v8-garbage-collection 新的 space 只收集新创建的短期数据和旧的 space 包含所有引用的数据结构,在您的情况下应该是最佳选择。

我在尝试使用 VSCode 进行调试时遇到了这个问题,所以只想添加这是将参数添加到调试设置的方法。

您可以将其添加到 launch.json 中配置的 runtimeArgs 属性。

参见下面的示例。

{
"version": "0.2.0",
"configurations": [{
        "type": "node",
        "request": "launch",
        "name": "Launch Program",
        "program": "${workspaceRoot}\server.js"
    },
    {
        "type": "node",
        "request": "launch",
        "name": "Launch Training Script",
        "program": "${workspaceRoot}\training-script.js",
        "runtimeArgs": [
            "--max-old-space-size=4096"
        ]
    }
]}

即使在设置了 --max-old-space-size 之后,我也在为此苦苦挣扎。

然后我意识到需要在 karma 脚本之前添加选项 --max-old-space-size。

最好同时指定语法 --max-old-space-size 和 --max_old_space_size 我的 karma 脚本:

node --max-old-space-size=8192 --optimize-for-size --max-executable-size=8192  --max_old_space_size=8192 --optimize_for_size --max_executable_size=8192 node_modules/karma/bin/karma start --single-run --max_new_space_size=8192   --prod --aot

参考https://github.com/angular/angular-cli/issues/1652

升级节点到最新版本。我在节点 6.6 上出现此错误并升级到 8.9.4,问题消失了。

以防万一它可以帮助人们在使用产生大量日志记录的 nodejs 应用程序时遇到这个问题,一位同事通过将标准输出传输到文件解决了这个问题。

我在进行 AOT angular 构建时遇到了类似的问题。以下命令对我有帮助。

npm install -g increase-memory-limit
increase-memory-limit

来源:https://geeklearning.io/angular-aot-webpack-memory-trick/

以防万一有人在无法直接设置节点属性的环境中遇到此问题(在我的例子中是构建工具):

NODE_OPTIONS="--max-old-space-size=4096" node ...

如果您不能在命令行上传递它们,您可以使用环境变量设置节点选项。

Steps to fix this issue (In Windows) -

  1. 打开命令提示符并输入 %appdata% 回车
  2. 导航至 %appdata% > npm 文件夹
  3. 在您喜欢的编辑器中打开或编辑 ng.cmd
  4. --max_old_space_size=8192 添加到 IF 和 ELSE 块

您的 node.cmd 文件在更改后如下所示:

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe" "--max_old_space_size=8192" "%~dp0\node_modules\@angular\cli\bin\ng" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node "--max_old_space_size=8192" "%~dp0\node_modules\@angular\cli\bin\ng" %*
)

如果你想全局增加节点的内存使用 - 不仅仅是单个脚本,你可以导出环境变量,像这样:
export NODE_OPTIONS=--max_old_space_size=4096

那么当 运行 构建时你不需要玩文件 npm run build.

这里有一些标志值,用于添加一些关于如何在启动节点服务器时允许更多内存的附加信息。

1GB - 8GB

#increase to 1gb
node --max-old-space-size=1024 index.js

#increase to 2gb
node --max-old-space-size=2048 index.js 

#increase to 3gb
node --max-old-space-size=3072 index.js

#increase to 4gb
node --max-old-space-size=4096 index.js

#increase to 5gb
node --max-old-space-size=5120 index.js

#increase to 6gb
node --max-old-space-size=6144 index.js

#increase to 7gb
node --max-old-space-size=7168 index.js

#increase to 8gb 
node --max-old-space-size=8192 index.js 

在我的例子中,我在以前版本的节点上有 运行 npm install,几天后我升级了节点版本和 ram npm install 几个模块。在此之后我收到了这个错误。 为了解决这个问题,我从每个项目中删除了 node_module 文件夹并再次 运行 npm install

希望这可以解决问题。

注意:这是在我的本地机器上发生的,它只在本地机器上得到修复。

如果您尝试启动的不是 node 本身,而是其他软件,例如 webpack 您可以使用环境变量和 cross-env 包:

$ cross-env NODE_OPTIONS='--max-old-space-size=4096' \
  webpack --progress --config build/webpack.config.dev.js

我最近遇到了同样的问题并遇到了这个线程,但我的问题是 React App。节点启动命令中的以下更改解决了我的问题。

语法

node --max-old-space-size=<size> path-to/fileName.js

例子

node --max-old-space-size=16000 scripts/build.js

为什么 max-old-space-size 的大小是 16000?

基本上,它取决于分配给该线程的内存和您的节点设置。

如何验证并给出合适的尺码?

这基本上是留在我们的引擎中v8。下面的代码可以帮助您了解本地节点 v8 引擎的堆大小。

const v8 = require('v8');
const totalHeapSize = v8.getHeapStatistics().total_available_size;
const totalHeapSizeGb = (totalHeapSize / 1024 / 1024 / 1024).toFixed(2);
console.log('totalHeapSizeGb: ', totalHeapSizeGb);

如果你想为节点(windows)全局更改内存,请转到高级系统设置 -> 环境变量 -> 新用户变量

variable name = NODE_OPTIONS
variable value = --max-old-space-size=4096

对于 angular 项目捆绑,我已 将以下行添加到 我的 pakage.json 文件中脚本部分。

"build-prod": "node --max_old_space_size=5120 ./node_modules/@angular/cli/bin/ng build --prod --base-href /"

现在,为了捆绑我的代码,我使用 npm run build-prod 而不是 ng build --requiredFlagsHere

希望对您有所帮助!

我只是想补充一点,在某些系统中,即使使用 --max-old-space-size 增加节点内存限制,这也是不够的,并且会出现这样的 OS 错误:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

在这种情况下,可能是因为您达到了每个进程的最大 mmap。

您可以通过 运行ning

查看 max_map_count
sysctl vm.max_map_count

并增加运行宁

sysctl -w vm.max_map_count=655300

并通过添加此行将其修复为在重启后不被重置

vm.max_map_count=655300

/etc/sysctl.conf 文件中。

查看 here 了解更多信息。

分析错误的一个好方法是通过运行与strace

的过程
strace node --max-old-space-size=128000 my_memory_consuming_process.js

我的 EC2 实例 t2.micro 刚刚遇到同样的问题,它有 1 GB 内存。

我通过使用 this url 创建交换文件并设置以下环境变量解决了这个问题。

export NODE_OPTIONS=--max_old_space_size=4096

问题终于解决了。

希望对以后有所帮助。

就我而言,我将 node.js 版本升级到最新版本(12.8.0 版),效果非常好。

对于像我这样没有找到任何适合此错误解决方案的其他初学者,请检查安装的 node 版本(x32、x64、x86)。我有一个 64 位 CPU 并且我安装了 x86 节点版本,这导致了 CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 错误。

您还可以更改 Window 的环境变量:

 $env:NODE_OPTIONS="--max-old-space-size=8192"

对于Angular,我就是这样修复的

Package.json 中,在 script 标签内添加这个

"scripts": {
  "build-prod": "node --max_old_space_size=5048 ./node_modules/@angular/cli/bin/ng build --prod",
},

现在 terminal/cmd 不再使用 ng build --prod 而是使用

npm run build-prod

如果您只想将此配置用于 build,只需从所有 3 个位置删除 --prod

最近,在我的一个项目中 运行 遇到了同样的问题。尝试了几件事,任何人都可以尝试作为调试来确定根本原因:

  1. 正如大家所建议的,通过添加以下命令增加节点中的内存限制:

    {
       "scripts":{
          "server":"node --max-old-space-size={size-value} server/index.js"
       }
    }
    

这里 size-value 我为我的应用程序定义的是 1536(因为我的 kubernetes pod 内存是 2 GB 限制,请求 1.5 GB)

因此请始终根据您的前端 infrastructure/architecture 限制定义 size-value(略小于限制)

在上面的命令中有一个严格的标注,在 node 命令之后使用 --max-old-space-size 而不是在文件名 server/index.js.[=38 之后=]

  1. 如果您有 ngnix 配置文件,请检查以下内容:

    • worker_connections: 16384(对于重型前端应用程序) [nginx 默认是每个 worker512 个连接,这对于现代应用程序来说太低了]

    • 使用:epoll(高效的方法)[nginx支持多种连接处理方式]

    • http: 添加以下内容,让您的工作人员从忙于处理一些不需要的任务中解脱出来。 (client_body_timeout , reset_timeout_connection , client_header_timeout,keepalive_timeout ,send_timeout).

  2. 删除所有 logging/tracking 工具,如 APM , Kafka , UTM tracking, Prerender (SEO) 等 中间件 或关闭。

  3. 现在进行代码级调试:在您的主 server 文件中,删除不需要的 console.log,它只是打印一条消息。

  4. 现在检查每个服务器路由,即 app.get() , app.post() ... 以下情况:

  • data => if(data) res.send(data) // 你真的需要等待数据还是 api returns 我必须等待的响应? , 如果没有则修改为:
data => res.send(data) // this will not block your thread, apply everywhere where it's needed
  • else 部分:如果没有错误,那么只需 return res.send({}) , NO console.log here.

  • 错误部分:有些人定义为errorerr,这会造成混淆和错误。像这样:

    `error => { next(err) } // here err is undefined`
    
     `err => {next(error) } // here error is undefined`
    
     `app.get(API , (re,res) =>{
         error => next(error) // here next is not defined
      })`
    
  • 使用 npx depcheck 命令删除 winston , elastic-epm-node 其他未使用的库。

  • 在 axios 服务文件中,检查方法和日志是否正确:

      if(successCB) console.log("success") successCB(response.data) // here it's wrong statement, because on success you are just logging and then `successCB` sending  outside the if block which return in failure case also.
    
  • 避免在可访问的大型数据集上使用 stringify , parse 等。 (我也可以在上面显示的日志中看到。

  1. 最后但同样重要的是,每次当您的应用程序崩溃或 pods 重新启动时,请检查日志。在日志中专门查找此部分:Security context 这将为您提供原因、地点和谁是崩溃背后的罪魁祸首

使用选项--optimize-for-size。它将专注于使用更少的内存。

如果给定的任何答案都不适合您,请检查您安装的节点是否与您的系统兼容(即 32 位或 64 位)。通常这种类型的错误是由于不兼容的节点和 OS 版本而发生的,terminal/system 不会告诉你,但会让你给出内存不足的错误。

如果您的内存或 RAM 有限,请执行以下命令。

ng serve --source-map=false

它将能够启动该应用程序。对于我的示例,它需要 16gb RAM。但是我可以 运行 使用 8gb 内存。

检查您是否没有在 64 位计算机上安装 32 位版本的节点。如果您是 64 位或 32 位计算机上的 运行 节点,则 nodejs 文件夹应分别位于 Program Files 和 Program Files (x86) 中。

我会提到两种解决方案。

我的解决方案:在我的例子中,我将它添加到我的环境变量中:

export NODE_OPTIONS=--max_old_space_size=20480

但即使我重新启动计算机,它仍然无法正常工作。我的项目文件夹在 d:\ disk 中。所以我将我的项目删除到 c:\ 磁盘并且它工作了。

我队友的解决方案:package.json配置也有效。

"start": "rimraf ./build && react-scripts --expose-gc --max_old_space_size=4096 start",

我在 AWS Elastic Beanstalk 上遇到了这个错误,将实例类型从 t3.micro(免费套餐)升级到 t3.small 修复了错误

Unix (Mac OS)

  1. 打开终端并使用 nano 打开我们的 .zshrc 文件 (this将创建一个,如果一个不存在):

    nano ~/.zshrc

  2. 通过将以下行添加到我们当前打开的 .zshrc 来更新我们的 NODE_OPTIONS 环境变量文件:

    export NODE_OPTIONS=--max-old-space-size=8192 # increase node memory limit

请注意,只要我们的系统有足够的内存,我们可以将传入的兆字节数设置为任意值(这里我们传入的是 8192 兆字节,大约是 8 GB).

  1. ctrl + x 保存并退出 nano,然后 y 同意,最后 enter 保存更改。

  2. 关闭重新打开 终端以确保我们的更改已被认可。

  3. 我们可以打印我们的.zshrc文件的内容,看看我们的更改是否像这样被保存: cat ~/.zshrc.

Linux (Ubuntu)

  1. 打开终端并使用 nano 打开 .bashrc 文件,如下所示:

    nano ~/.bashrc

其余步骤与上面的Mac步骤类似,除了我们很可能默认使用~/.bashrc 而不是 ~/.zshrc)。因此需要替换这些值!

Link to Nodejs Docs

我今天遇到了同样的问题。我的问题是,我试图将大量数据导入 NextJS 项目中的数据库。

所以我所做的是,我安装了 win-node-env 软件包,如下所示:

yarn add win-node-env

因为我的开发机是Windows。我在本地而不是在全球安装它。您也可以像这样全局安装它:yarn global add win-node-env

然后在我的 NextJS 项目的 package.json 文件中,我添加了另一个启动脚本,如下所示:

"dev_more_mem": "NODE_OPTIONS=\"--max_old_space_size=8192\" next dev"

在这里,我正在传递节点选项,即。设置 8GB 作为限制。 所以我的 package.json 文件看起来像这样:

{
  "name": "my_project_name_here",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "dev_more_mem": "NODE_OPTIONS=\"--max_old_space_size=8192\" next dev",
    "build": "next build",
    "lint": "next lint"
  },
  ......
}

然后我 运行 是这样的:

yarn dev_more_mem

对我来说,我只在我的开发机器上遇到过这个问题(因为我正在导入大数据)。因此这个解决方案。想分享这个,因为它可能对其他人有用。