NodeJS 内存增长 - (系统)内存泄漏?
NodeJS memory growth - Memory leak in (system)?
我在我们的实时环境中遇到奇怪的内存泄漏,堆中的 (system)
个对象不断增长。
堆转储
这是内存使用量增长到 800MB 的内存转储:
请注意,此内存保留在 Generator
对象中。 TBH,我不知道那是什么。
通过global.gc();
手动触发垃圾收集通常释放大约 10MB 的内存。 (但是重复触发gc没有影响)
应用程序崩溃
应用程序大约每 12 小时崩溃一次,并出现以下错误:
2019-11-15T08:29:01.174417825Z 08:29:01 0|. | <--- Last few GCs --->
2019-11-15T08:29:01.176095829Z 08:29:01 0|. | [47:0x55ac8cdf3dc0] 65890519 ms: Scavenge 912.3 (929.2) -> 909.3 (929.2) MB, 6.7 / 0.0 ms (average mu = 0.987, current mu = 0.987) allocation failure
2019-11-15T08:29:01.177260332Z 08:29:01 0|. | [47:0x55ac8cdf3dc0] 65890653 ms: Scavenge 912.9 (929.2) -> 910.0 (929.2) MB, 10.1 / 0.0 ms (average mu = 0.987, current mu = 0.987) allocation failure
2019-11-15T08:29:01.178027234Z 08:29:01 0|. | [47:0x55ac8cdf3dc0] 65890701 ms: Scavenge 913.6 (929.2) -> 910.6 (929.2) MB, 6.3 / 0.0 ms (average mu = 0.987, current mu = 0.987) allocation failure
2019-11-15T08:29:01.183406747Z 08:29:01 0|. | <--- JS stacktrace --->
2019-11-15T08:29:01.184194849Z 08:29:01 0|. | ==== JS stack trace =========================================
2019-11-15T08:29:01.184939851Z 08:29:01 0|. | 0: ExitFrame [pc: 0x55ac88379c39]
2019-11-15T08:29:01.185674553Z 08:29:01 0|. | Security context: 0x064ed4d408d1 <JSObject>
2019-11-15T08:29:01.187183757Z 08:29:01 0|. | 1: /* anonymous */ [0x34eb4a55a171] [/usr/src/app/node_modules/express-validator/src/chain/context-runner-impl.js:~25] [pc=0x34ab58ee2e1f](this=0x3c92e4142b89 <ContextRunnerImpl map = 0x13b2881d9399>)
2019-11-15T08:29:01.188904261Z 08:29:01 0|. | 2: next [0x64ed4d63621](this=0x34eb4a55a569 <JSGenerator>)
2019-11-15T08:29:01.190831866Z 08:29:01 0|. | 3: /* anonymous */(aka /* anonymous */) [0x34eb4a55a201] [/usr/src/app/node_modules/expr...
2019-11-15T08:29:01.195055876Z 08:29:01 0|. | FATAL ERROR: invalid array length Allocation failed - JavaScript heap out of memory
2019-11-15T08:29:01.396743877Z 2019-11-15T08:29:01: PM2 log: App [.:0] exited with code [0] via signal [SIGABRT]
2019-11-15T08:29:01.400481986Z 08:29:01 PM2 | App [.:0] exited with code [0] via signal [SIGABRT]
2019-11-15T08:29:01.403095193Z 2019-11-15T08:29:01: PM2 log: App [.:0] starting in -fork mode-
2019-11-15T08:29:01.406294501Z 08:29:01 PM2 | App [.:0] starting in -fork mode-
2019-11-15T08:29:01.419367433Z 2019-11-15T08:29:01: PM2 log: App [.:0] online
2019-11-15T08:29:01.422309441Z 08:29:01 PM2 | App [.:0] online
2019-11-15T08:29:03.279070252Z 08:29:03 0|. | server started at http://localhost:80
这发生在大约 1.2 GB 的进程内存使用情况下(此时机器 运行 占用 ~67% 的内存)。这本身就很奇怪,因为我使用以下命令 运行 服务器:
pm2 start . --no-daemon --node-args=\"--max-old-space-size=2048 --max-semi-space-size=4 --expose_gc\" --max-memory-restart 1536M
因此节点应该有 2GB 可用空间并且 pm2 应该在进程达到限制之前很好地重新启动。
申请背景
该应用程序是用 TypeScript 编写的 json api。它使用 express
来处理请求(每分钟约 3k)并使用 node-mssql
来查询 MS SQL 服务器。
此外,它通过 bee-queue
包异步处理作业(执行额外的 sql 查询)。
因此,我通过编译为 es2017
而不是 es6
来解决问题。
详情:
我注意到当我将 async
函数传递给 express
路由器时出现了问题。然而,返回 Promise
的函数没有问题。
我发现问题在于,当将 TypeScript 代码编译为 es6
时,它会转换
async function() {...
到
__awaiter(this, void 0, void 0, function* () {...
这就是 Generator
发挥作用的地方。看起来 express
在处理请求后无法正确释放它。由于 es2017
支持 async
功能,这不再是问题。
此外,我不得不删除包 express-validator
,因为它们的验证中间件被编译为相同的 Generator
结构。
我在我们的实时环境中遇到奇怪的内存泄漏,堆中的 (system)
个对象不断增长。
堆转储
这是内存使用量增长到 800MB 的内存转储:
请注意,此内存保留在 Generator
对象中。 TBH,我不知道那是什么。
通过global.gc();
手动触发垃圾收集通常释放大约 10MB 的内存。 (但是重复触发gc没有影响)
应用程序崩溃
应用程序大约每 12 小时崩溃一次,并出现以下错误:
2019-11-15T08:29:01.174417825Z 08:29:01 0|. | <--- Last few GCs --->
2019-11-15T08:29:01.176095829Z 08:29:01 0|. | [47:0x55ac8cdf3dc0] 65890519 ms: Scavenge 912.3 (929.2) -> 909.3 (929.2) MB, 6.7 / 0.0 ms (average mu = 0.987, current mu = 0.987) allocation failure
2019-11-15T08:29:01.177260332Z 08:29:01 0|. | [47:0x55ac8cdf3dc0] 65890653 ms: Scavenge 912.9 (929.2) -> 910.0 (929.2) MB, 10.1 / 0.0 ms (average mu = 0.987, current mu = 0.987) allocation failure
2019-11-15T08:29:01.178027234Z 08:29:01 0|. | [47:0x55ac8cdf3dc0] 65890701 ms: Scavenge 913.6 (929.2) -> 910.6 (929.2) MB, 6.3 / 0.0 ms (average mu = 0.987, current mu = 0.987) allocation failure
2019-11-15T08:29:01.183406747Z 08:29:01 0|. | <--- JS stacktrace --->
2019-11-15T08:29:01.184194849Z 08:29:01 0|. | ==== JS stack trace =========================================
2019-11-15T08:29:01.184939851Z 08:29:01 0|. | 0: ExitFrame [pc: 0x55ac88379c39]
2019-11-15T08:29:01.185674553Z 08:29:01 0|. | Security context: 0x064ed4d408d1 <JSObject>
2019-11-15T08:29:01.187183757Z 08:29:01 0|. | 1: /* anonymous */ [0x34eb4a55a171] [/usr/src/app/node_modules/express-validator/src/chain/context-runner-impl.js:~25] [pc=0x34ab58ee2e1f](this=0x3c92e4142b89 <ContextRunnerImpl map = 0x13b2881d9399>)
2019-11-15T08:29:01.188904261Z 08:29:01 0|. | 2: next [0x64ed4d63621](this=0x34eb4a55a569 <JSGenerator>)
2019-11-15T08:29:01.190831866Z 08:29:01 0|. | 3: /* anonymous */(aka /* anonymous */) [0x34eb4a55a201] [/usr/src/app/node_modules/expr...
2019-11-15T08:29:01.195055876Z 08:29:01 0|. | FATAL ERROR: invalid array length Allocation failed - JavaScript heap out of memory
2019-11-15T08:29:01.396743877Z 2019-11-15T08:29:01: PM2 log: App [.:0] exited with code [0] via signal [SIGABRT]
2019-11-15T08:29:01.400481986Z 08:29:01 PM2 | App [.:0] exited with code [0] via signal [SIGABRT]
2019-11-15T08:29:01.403095193Z 2019-11-15T08:29:01: PM2 log: App [.:0] starting in -fork mode-
2019-11-15T08:29:01.406294501Z 08:29:01 PM2 | App [.:0] starting in -fork mode-
2019-11-15T08:29:01.419367433Z 2019-11-15T08:29:01: PM2 log: App [.:0] online
2019-11-15T08:29:01.422309441Z 08:29:01 PM2 | App [.:0] online
2019-11-15T08:29:03.279070252Z 08:29:03 0|. | server started at http://localhost:80
这发生在大约 1.2 GB 的进程内存使用情况下(此时机器 运行 占用 ~67% 的内存)。这本身就很奇怪,因为我使用以下命令 运行 服务器:
pm2 start . --no-daemon --node-args=\"--max-old-space-size=2048 --max-semi-space-size=4 --expose_gc\" --max-memory-restart 1536M
因此节点应该有 2GB 可用空间并且 pm2 应该在进程达到限制之前很好地重新启动。
申请背景
该应用程序是用 TypeScript 编写的 json api。它使用 express
来处理请求(每分钟约 3k)并使用 node-mssql
来查询 MS SQL 服务器。
此外,它通过 bee-queue
包异步处理作业(执行额外的 sql 查询)。
因此,我通过编译为 es2017
而不是 es6
来解决问题。
详情:
我注意到当我将 async
函数传递给 express
路由器时出现了问题。然而,返回 Promise
的函数没有问题。
我发现问题在于,当将 TypeScript 代码编译为 es6
时,它会转换
async function() {...
到
__awaiter(this, void 0, void 0, function* () {...
这就是 Generator
发挥作用的地方。看起来 express
在处理请求后无法正确释放它。由于 es2017
支持 async
功能,这不再是问题。
此外,我不得不删除包 express-validator
,因为它们的验证中间件被编译为相同的 Generator
结构。