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

WebpackDevServer内存不足时如何强制Node.js使用Swap空间避免崩溃?

问题:如何让Node.js在内存不足时使用Swap而非直接崩溃?

背景与现象

我在运行一个复杂React应用的WebpackDevServer时,核心执行代码为new WebpackDevServer(compiler, config).listen(),但经常遇到FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory错误导致崩溃。

我的package.json中start脚本配置如下:
package.json

{
    ...
    "scripts": {
      "start": "node scripts/start.js",
      ...
    }
    ...
}

我尝试过用yarn start --max-old-space-size=32000或node scripts/start.js --max-old-space-size=32000启动,但不确定yarn是否正确传递了参数。

通过watch -n1 free -m监控内存发现:当物理内存剩余约5.7GB时,WebpackDevServer会在1分钟内持续消耗内存,直到物理内存剩余仅200MB左右触发崩溃,期间Swap空间完全没有被使用。只有关闭其他大部分应用,让物理内存剩余约1GB时,才能成功启动服务。

系统信息:

[caleb@caleb-manjaro ui]$ lsb_release -a
LSB Version:    n/a
Distributor ID: ManjaroLinux
Description:    Manjaro Linux
Release:    23.0.0
Codename:   Uranos

完整启动脚本如下:

'use strict';

process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';

process.on('unhandledRejection', (err) => {
    throw err;
});

require('../config/env');

const fs = require('fs');
const chalk = require('react-dev-utils/chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const {
    choosePort,
    createCompiler,
    prepareProxy,
    prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const semver = require('semver');
const paths = require('../config/paths');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
const getClientEnvironment = require('../config/env');
const react = require(require.resolve('react', { paths: [paths.appPath] }));

const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;

if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
    process.exit(1);
}

const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';

if (process.env.HOST) {
    console.log(
        chalk.cyan(
            `Attempting to bind to HOST environment variable: ${chalk.yellow(
                chalk.bold(process.env.HOST)
            )}`
        )
    );
    console.log(
        `If this was unintentional, check that you haven't mistakenly set it in your shell.`
    );
    console.log(
        `Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`
    );
    console.log();
}

const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
    .then(() => {
        return choosePort(HOST, DEFAULT_PORT);
    })
    .then((port) => {
        if (port == null) {
            return;
        }

        const config = configFactory('development');
        const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
        const appName = require(paths.appPackageJson).name;

        const useTypeScript = fs.existsSync(paths.appTsConfig);
        const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
        const urls = prepareUrls(
            protocol,
            HOST,
            port,
            paths.publicUrlOrPath.slice(0, -1)
        );
        const devSocket = {
            warnings: (warnings) =>
                devServer.sockWrite(devServer.sockets, 'warnings', warnings),
            errors: (errors) =>
                devServer.sockWrite(devServer.sockets, 'errors', errors),
        };
        const compiler = createCompiler({
            appName,
            config,
            devSocket,
            urls,
            useYarn,
            useTypeScript,
            tscCompileOnError,
            webpack,
        });
        const proxySetting = require(paths.appPackageJson).proxy;
        const proxyConfig = prepareProxy(
            proxySetting,
            paths.appPublic,
            paths.publicUrlOrPath
        );
        const serverConfig = createDevServerConfig(
            proxyConfig,
            urls.lanUrlForConfig
        );
        const devServer = new WebpackDevServer(compiler, serverConfig);
        devServer.listen(port, HOST, (err) => {
            if (err) {
                return console.log(err);
            }
            if (isInteractive) {
                clearConsole();
            }

            if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
                console.log(
                    chalk.yellow(
                        `Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
                    )
                );
            }

            console.log(chalk.cyan('Starting the development server...\n'));
        });

        ['SIGINT', 'SIGTERM'].forEach(function (sig) {
            process.on(sig, function () {
                devServer.close();
                process.exit();
            });
        });

        if (process.env.CI !== 'true') {
            process.stdin.on('end', function () {
                devServer.close();
                process.exit();
            });
        }
    })
    .catch((err) => {
        if (err && err.message) {
            console.log(err.message);
        }
        process.exit(1);
    });

核心问题

是否有办法确保Node.js在内存不足时分配Swap空间,而非直接崩溃?


解决方案

1. 先确保Node.js参数正确传递

Yarn传参的正确方式

Yarn不会自动把命令行参数传递给脚本里的Node进程,必须用--分隔参数:

yarn start -- --max-old-space-size=32000

或者直接修改package.json的start脚本,把参数写死,这样不管用yarn还是npm启动都能生效:

{
  "scripts": {
    "start": "node --max-old-space-size=32000 scripts/start.js"
  }
}

2. 调整系统内存调度策略,让系统更早使用Swap

Node.js本身不直接控制Swap使用,这是操作系统的内存管理范畴,可以通过修改Linux内核参数调整:

修改vm.swappiness参数

swappiness值越高,系统越倾向于使用Swap。默认值通常是60,临时调高可以执行:

sudo sysctl vm.swappiness=80

要永久生效,编辑/etc/sysctl.conf,添加或修改:

vm.swappiness=80

然后执行sudo sysctl -p让配置生效。

调整vm.min_free_kbytes参数

这个参数定义系统必须保留的最小空闲内存量,降低它可以让系统在剩余内存更少时才触发OOM Killer,同时更早使用Swap。先查看当前值:

cat /proc/sys/vm/min_free_kbytes

临时修改(比如设为102400,即100MB):

sudo sysctl vm.min_free_kbytes=102400

永久修改同样编辑/etc/sysctl.conf,添加对应配置后执行sudo sysctl -p。

3. 优化Webpack构建减少内存占用

从根源上降低内存需求更有效,针对复杂应用可以做这些优化:

  • 启用Webpack的cache配置,缓存构建结果,减少重复编译的内存消耗
  • 用splitChunks拆分大型代码块,优化分包逻辑
  • 禁用开发环境不必要的loader和plugin,只保留必需工具
  • 开启webpack-dev-server的lazy模式,按需编译代码

4. 验证Swap是否正常工作

修改系统参数后,可以这样验证:

  • 启动WebpackDevServer,同时用watch -n1 free -m监控内存变化
  • 观察物理内存不足时,Swap的used值是否开始增加
  • 如果Swap仍未被使用,先检查Swap分区是否正常挂载:
swapon --show

如果没有输出,说明Swap未启用,需要先挂载Swap分区或创建Swap文件。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 11:50:54