Table of Contents

Node.js 构建内存溢出排查与优化

63 2.2~2.9 min 1010

前言

本站主题项目使用 Vite 构建,具有多个页面入口,在内存里复制了多份图标资源,并为静态资源生成 gzip、Brotli 和 Zstandard 预压缩文件。在 CI 中构建完整发布包时,曾遇到内存不足的问题。

定位问题

如果日志中出现以下错误:

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

说明是 Node.js 触及了 V8 堆上限,可尝试调整 V8 堆上限解决。

如果日志中只有 Killed 或进程以退出码 137 结束,说明进程收到了 SIGKILL。常见原因是运行器(Runner)或容器内存不足,可尝试添加交换分区解决。

获取环境信息

可以让 Linux CI 先运行以下指令,输出相关信息:

# 查看内存和交换空间情况
free -h

# 查看已启用的交换空间详情
swapon --show

# 查看 /tmp 目录的磁盘分区情况
# 方便后面创建临时交换分区
df -h /tmp

CI 任务可能运行在容器中。此时 free -h 看到的可能是宿主机内存,还需检查 cgroup 限制:

# 查看当前 cgroup(你所在的容器)的内存硬限制(上限)
cat /sys/fs/cgroup/memory.max

# 查看当前 cgroup 实际使用的内存量
cat /sys/fs/cgroup/memory.current

memory.max 输出数字时,单位为字节。输出 max 表示未设置硬上限。

cgroup v1 可检查 /sys/fs/cgroup/memory/memory.limit_in_bytes/sys/fs/cgroup/memory/memory.usage_in_bytes

获取进程信息

还可以使用以下命令记录单次构建的最大常驻内存:

/usr/bin/time -v pnpm build

pnpm build 是我使用的构建指令,需要将其替换为实际使用的构建指令。

构建结束后显示的 Maximum resident set size 就是整个进程在生命周期内占用的最大物理内存。可以用于比较配置调整前后的内存峰值。

解决问题

调整 V8 堆上限

对于以下错误,可以通过调整 V8 堆上限来解决。

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

本站主题项目在 package.json 中使用以下构建命令:
(同时在 pnpm-workspace.yaml 设定 shellEmulator: true 以兼容多平台):

{
  "scripts": {
    "build": "NODE_OPTIONS=--max-old-space-size=10240 vite build"
  }
}

使用 --max-old-space-size=10240 将 V8 引擎老生代堆空间上限设为 10240 MiB。相关文档

提高 V8 堆上限前应使用 free -h 查看内存和交换空间情况,确保有足够的内存空间,否则依旧会触发 OOM Killer 杀掉构建进程。如果发现内存不够用,可以选择添加交换分区解决。

添加交换分区

本站主题项目的 GitHub Actions 在构建前执行:

- name: Set Up Swap
  run: |
    # 定义交换文件大小
    # 单位 GB,按需修改
    SIZE=12  # GB,按需修改

    # 使用 mktemp 在 /tmp 下安全创建临时文件名
    SWAP=$(mktemp /tmp/swap-XXXXXX)

    # 以 root 权限一次性完成:预分配空间、设置权限、格式化并启用交换空间
    # fallocate -l ${SIZE}G $SWAP  创建指定大小的文件
    # chmod 600 $SWAP              仅允许 root 读写,防止其他用户访问
    # mkswap $SWAP                 将文件格式化为 swap 格式
    # swapon $SWAP                 立即启用该交换空间
    sudo bash -c "fallocate -l ${SIZE}G $SWAP && chmod 600 $SWAP && mkswap $SWAP && swapon $SWAP"

    # 查看内存和 swap 状态,确认交换空间已成功添加
    free -h

这段工作流创建并启用了 12 GiB 交换文件。

swap 使用硬盘空间换内存,添加的交换空间相较于物理内存速度较慢。并且创建前应该使用 df -h /tmp 检查剩余硬盘空间,空间不足会创建失败。

部分托管 CI 和容器不允许执行 swapon。此时应该调整构建配置(如降低并发数)以降低峰值内存或更换更大内存的运行器。

参考资料


0