/** * 构建时根据可用内存 / CPU 计算 heap 与 webpack.parallelism。 * 优先读取 cgroup 上限(Docker / K8s),避免误用宿主机总内存。 * * 可手动覆盖:NAVSPHERE_HEAP_MB、NAVSPHERE_WEBPACK_PARALLELISM */ const os = require("os"); const fs = require("fs"); function readCgroupMemoryLimitBytes() { try { const v2 = "/sys/fs/cgroup/memory.max"; if (fs.existsSync(v2)) { const s = fs.readFileSync(v2, "utf8").trim(); if (s && s !== "max") { const n = parseInt(s, 10); if (n > 0 && n < 1e15) return n; } } const v1 = "/sys/fs/cgroup/memory/memory.limit_in_bytes"; if (fs.existsSync(v1)) { const n = parseInt(fs.readFileSync(v1, "utf8"), 10); const host = os.totalmem(); if (n > 0 && n < 1e15 && n <= host * 4) return n; } } catch { /* ignore */ } return null; } function effectiveTotalBytes() { const cgroup = readCgroupMemoryLimitBytes(); const host = os.totalmem(); if (cgroup == null) return host; return Math.min(cgroup, host); } function parsePositiveInt(name) { const v = process.env[name]; if (v == null || v === "") return null; const n = parseInt(v, 10); return Number.isFinite(n) && n > 0 ? n : null; } function getBuildProfile() { const total = effectiveTotalBytes(); const memMB = total / 1024 / 1024; const cpus = typeof os.availableParallelism === "function" ? os.availableParallelism() : os.cpus().length; const heapOverride = parsePositiveInt("NAVSPHERE_HEAP_MB"); const parOverride = parsePositiveInt("NAVSPHERE_WEBPACK_PARALLELISM"); // V8 堆:取「比例」与「为系统预留」二者中较小者,避免占满整机触发 OOM killer const byRatio = memMB * 0.32; const reserveMB = memMB < 1024 ? 180 : 220; const byHeadroom = Math.max(0, memMB - reserveMB); let heapMB = Math.floor(Math.min(byRatio, byHeadroom)); heapMB = Math.min(4096, Math.max(384, heapMB)); if (heapOverride != null) { heapMB = Math.min(8192, heapOverride); } // Webpack:内存或 CPU 偏低时保持 1;资源充足时适度提高(仍限制上限) let parallelism = 1; if (parOverride != null) { parallelism = Math.min(8, parOverride); } else { if (memMB >= 1536 && cpus >= 2) { parallelism = Math.min(4, Math.max(1, Math.floor(cpus / 2))); } if (memMB >= 4096 && cpus >= 4) { parallelism = Math.min(6, Math.max(1, Math.floor(cpus * 0.75))); } if (memMB < 1200) { parallelism = 1; } } return { heapMB, parallelism, totalBytes: total, cpus, memMB: Math.round(memMB * 10) / 10, }; } module.exports = { getBuildProfile, effectiveTotalBytes, };