You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署Node.js+Express集成Google APIs至EC2时遇JavaScript堆内存溢出

Node.js + Express 集成Google APIs部署EC2时内存溢出问题

我正在构建一个基于Node.js和Express集成Google APIs的服务,本地环境运行一切正常,但部署到EC2实例时,出现JavaScript heap out of memory错误,导致应用崩溃。移除实例化OAuth2Client的代码后,应用即可正常运行。

问题详情

index.ts

import express from 'express';
import dotenv from 'dotenv';
import { google } from 'googleapis';

// ... 更多模板代码

const googleApiRoutes = require('./routes/googleApiRoutes');

app.use('/api/google-api', googleApiRoutes);

// 导致应用崩溃的实例化代码
export const OAuth2Client = new google.auth.OAuth2(
    process.env.GOOGLE_APIS_CLIENT_ID,
    process.env.GOOGLE_APIS_CLIENT_SECRET,
    process.env.GOOGLE_API_REDIRECT_URL
);

app.listen(process.env.PORT || 5007, () => {
    console.log(`[server]: Server is running at http://localhost:5007`);
});

package.json

{
  "name": "loopfive-services",
  "version": "1.0.0",
  "description": "A general service api useful for various projects",
  "main": "index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "prettier": "prettier --write .",
    "start": "nodemon index.ts"
  },
  "author": "Ilyes Hadji",
  "license": "ISC",
  "dependencies": {
    "@types/express": "^4.17.21",
    "@types/node": "^22.0.0",
    "body-parser": "^1.20.2",
    "config": "^3.3.6",
    "cookie-parser": "^1.4.6",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^4.18.3",
    "express-formidable": "^1.2.0",
    "express-session": "^1.17.2",
    "express-validator": "^7.0.1",
    "form-data": "^4.0.0",
    "formidable-serverless": "^1.1.1",
    "google-auth-library": "^9.14.0",
    "googleapis": "^142.0.0",
    "helmet": "^4.4.1",
    "mailgun.js": "^10.2.3",
    "method-override": "^3.0.0",
    "node-fetch": "^3.3.2",
    "nodemailer-mailgun-transport": "^2.1.5",
    "nodemon": "^3.1.4",
    "ts-node": "^10.9.2"
  },
  "devDependencies": {
    "typescript": "^5.5.4",
    "prettier": "^3.3.3"
  },
  "engines": {
    "node": ">=20.11.1"
  }
}

错误信息

<--- Last few GCs --->
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]: [51750:0x5b6f280]    13591 ms: Scavenge (reduce) 477.6 (486.1) -> 477.2 (486.4) MB, 5.85 / 0.00 ms  (average m>
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]: [51750:0x5b6f280]    13769 ms: Mark-Compact (reduce) 477.9 (486.4) -> 477.7 (487.4) MB, 95.09 / 0.01 ms  (+ 30>
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]: <--- JS stacktrace --->
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]: FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]: ----- Native stack trace -----
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  1: 0xb86ecf node::OOMErrorHandler(char const*, v8::OOMDetails const&) [node]
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  2: 0xef74d0 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  3: 0xef77b7 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails con>
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  4: 0x1109355  [node]
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  5: 0x11211d8 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectio>
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  6: 0x10f72f1 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType>
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  7: 0x10f8485 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationTyp>
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  8: 0x10d5ad6 v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::All>
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]:  9: 0x1531906 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [no>
Aug 27 04:11:56 ip-172-31-39-135 loopfive_services[51750]: 10: 0x196aef6  [node]
Aug 27 04:11:57 ip-172-31-39-135 loopfive_services[51749]: Aborted (core dumped)

解决方案

1. 增加Node.js内存限制

EC2实例(如t2.micro)默认内存较小,ts-node运行时加上googleapis库初始化的内存开销容易触发溢出。修改package.json的启动脚本,给Node.js分配更多内存:

"start": "nodemon --max-old-space-size=1024 index.ts"

生产环境建议先编译TypeScript为JavaScript再运行,进一步降低内存开销:

"build": "tsc",
"start:prod": "node --max-old-space-size=1024 dist/index.js"

参数--max-old-space-size=1024代表分配1GB内存,可根据实例内存调整(比如t2.small设2048)。

2. 延迟初始化OAuth2Client

不要在全局作用域直接实例化OAuth2Client,改为在需要使用的路由中懒加载,避免启动时一次性加载所有Google API相关资源:

// googleApiRoutes.ts
import { google } from 'googleapis';
import express from 'express';

const router = express.Router();

// 懒加载OAuth2Client
let oauthClient: google.auth.OAuth2 | null = null;

const getOAuthClient = () => {
  if (!oauthClient) {
    oauthClient = new google.auth.OAuth2(
      process.env.GOOGLE_APIS_CLIENT_ID,
      process.env.GOOGLE_APIS_CLIENT_SECRET,
      process.env.GOOGLE_API_REDIRECT_URL
    );
  }
  return oauthClient;
};

// 在路由中使用
router.get('/auth-url', (req, res) => {
  const client = getOAuthClient();
  const authUrl = client.generateAuthUrl({
    access_type: 'offline',
    scope: ['email', 'profile']
  });
  res.send({ authUrl });
});

module.exports = router;

3. 优化运行环境

  • 生产环境禁用nodemon:nodemon是开发工具,会额外占用内存,生产环境直接运行编译后的JS文件。
  • 检查EC2实例内存:执行free -m查看内存使用情况,如果剩余内存不足,考虑升级实例规格(比如从t2.micro升到t2.small)。
  • 清理无用依赖:检查package.json,移除不需要的依赖,减少启动时加载的资源量。

内容的提问来源于stack exchange,提问作者IlyesHadji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 03:07:03