性能与监控✅
本主题涵盖Node.js在生产环境中的性能监控、错误处理和内存优化等关键技术。
什么是负载?如何查看Node.js运行负载?
答案
核心概念:
负载(Load)是指系统在特定时间内正在运行和等待运行的进程数量。在Node.js中,监控负载包括CPU使用率、内存占用、事件循环延迟等关键指标。
监控方法:
Node.js提供了多种方式来监控系统和进程负载:
系统负载监控:
os.loadavg()- 获取系统1分钟、5分钟、15分钟的平均负载os.cpus()- 获取CPU核心信息os.totalmem()/os.freemem()- 获取内存信息
进程负载监控:
process.memoryUsage()- Node.js进程内存使用情况process.cpuUsage()- 进程CPU使用时间process.uptime()- 进程运行时间
示例代码:
- 负载监控演示
- 关键指标说明
// Node.js 负载监控示例 const os = require('os'); const cluster = require('cluster'); // 1. CPU 负载监控 function getCPULoad() { console.log('=== CPU 负载信息 ==='); // 获取CPU核心数 const cpuCount = os.cpus().length; console.log(`CPU 核心数: ${cpuCount}`); // 获取系统负载平均值 const loadavg = os.loadavg(); console.log(`系统负载 (1分钟): ${loadavg[0].toFixed(2)}`); console.log(`系统负载 (5分钟): ${loadavg[1].toFixed(2)}`); console.log(`系统负载 (15分钟): ${loadavg[2].toFixed(2)}`); // 负载率计算 (负载/核心数) const loadRate = (loadavg[0] / cpuCount * 100).toFixed(2); console.log(`负载率: ${loadRate}%`); } // 2. 内存使用监控 function getMemoryUsage() { console.log('\n=== 内存使用信息 ==='); // 系统内存 const totalMem = os.totalmem(); const freeMem = os.freemem(); const usedMem = totalMem - freeMem; console.log(`系统总内存: ${(totalMem / 1024 / 1024 / 1024).toFixed(2)} GB`); console.log(`系统空闲内存: ${(freeMem / 1024 / 1024 / 1024).toFixed(2)} GB`); console.log(`系统已用内存: ${(usedMem / 1024 / 1024 / 1024).toFixed(2)} GB`); console.log(`内存使用率: ${(usedMem / totalMem * 100).toFixed(2)}%`); // Node.js 进程内存 const memUsage = process.memoryUsage(); console.log('\n--- Node.js 进程内存 ---'); console.log(`RSS (物理内存): ${(memUsage.rss / 1024 / 1024).toFixed(2)} MB`); console.log(`Heap Used (堆已用): ${(memUsage.heapUsed / 1024 / 1024).toFixed(2)} MB`); console.log(`Heap Total (堆总计): ${(memUsage.heapTotal / 1024 / 1024).toFixed(2)} MB`); console.log(`External (外部内存): ${(memUsage.external / 1024 / 1024).toFixed(2)} MB`); } // 3. 进程负载监控 function getProcessLoad() { console.log('\n=== 进程负载信息 ==='); // 进程运行时间 const uptime = process.uptime(); console.log(`进程运行时间: ${(uptime / 3600).toFixed(2)} 小时`); // CPU 使用时间 const cpuUsage = process.cpuUsage(); console.log(`用户 CPU 时间: ${(cpuUsage.user / 1000).toFixed(2)} ms`); console.log(`系统 CPU 时间: ${(cpuUsage.system / 1000).toFixed(2)} ms`); // 事件循环延迟(简单估算) const start = Date.now(); setImmediate(() => { const delay = Date.now() - start; console.log(`事件循环延迟: ${delay} ms`); }); } // 4. 实时监控函数 function startMonitoring(interval = 5000) { console.log(`开始监控,间隔: ${interval}ms`); console.log('按 Ctrl+C 停止监控\n'); const monitor = () => { console.clear(); console.log(`=== Node.js 负载监控 (${new Date().toLocaleString()}) ===`); getCPULoad(); getMemoryUsage(); getProcessLoad(); console.log('\n' + '='.repeat(50)); }; // 立即执行一次 monitor(); // 定时监控 const timer = setInterval(monitor, interval); // 优雅关闭 process.on('SIGINT', () => { clearInterval(timer); console.log('\n监控已停止'); process.exit(0); }); } // 执行监控 if (require.main === module) { startMonitoring(3000); } module.exports = { getCPULoad, getMemoryUsage, getProcessLoad, startMonitoring };
负载指标解读:
// 负载率计算
const loadRate = (loadavg[0] / cpuCount) * 100;
// < 70%: 正常
// 70-90%: 需要关注
// > 90%: 高负载,可能影响性能
内存监控指标:
- RSS: 进程实际占用的物理内存
- Heap Used: V8引擎堆内存使用量
- Heap Total: V8引擎堆内存总量
- External: C++对象占用的内存
生产环境最佳实践:
- 设置负载报警阈值(通常CPU负载率>80%)
- 定期监控内存泄漏趋势
- 监控事件循环延迟,避免阻塞
- 使用PM2或类似工具进行进程管理
面试官视角:
该题考察候选人对Node.js性能监控的理解:
- 要点清单: 了解负载概念;掌握监控API使用;能设计监控方案;理解关键指标含义
- 加分项: 有生产环境监控经验;了解性能调优方法;能预防性能问题;有完整的监控体系
- 常见失误: 只知道基础API;不理解指标含义;缺乏实战经验;忽视监控的重要性
延伸阅读:
- Node.js性能监控最佳实践 — 官方性能分析指南
- 《Node.js实战》 — Node.js生产环境实践
Node.js有哪些错误类型,如何捕获错误?
答案
核心概念:
Node.js中的错误可以分为JavaScript标准错误、系统错误、用户定义错误等类型。掌握不同错误的特点和捕获方法是构建稳定应用的基础。
错误类型分类:
1. JavaScript标准错误:
Error- 通用错误基类SyntaxError- 语法错误TypeError- 类型错误ReferenceError- 引用错误RangeError- 范围错误
2. Node.js系统错误:
ENOENT- 文件或目录不存在EACCES- 权限被拒绝EMFILE- 打开文件过多EADDRINUSE- 地址已被使用
3. 异步错误:
- Promise rejection
- EventEmitter错误
- 回调函数错误
示例代码:
- 错误处理演示
- 错误处理模式
// Node.js 错误类型和捕获示例 const fs = require('fs').promises; const EventEmitter = require('events'); // 1. JavaScript 标准错误类型 function demonstrateJSErrors() { console.log('=== JavaScript 标准错误类型 ==='); try { // SyntaxError - 语法错误 console.log('SyntaxError 示例:'); // eval('let x = ;'); // 取消注释会抛出 SyntaxError // TypeError - 类型错误 console.log('TypeError 示例:'); const obj = null; // obj.someMethod(); // 取消注释会抛出 TypeError // ReferenceError - 引用错误 console.log('ReferenceError 示例:'); // console.log(undefinedVariable); // 取消注释会抛出 ReferenceError // RangeError - 范围错误 console.log('RangeError 示例:'); const arr = new Array(-1); // 会抛出 RangeError } catch (error) { console.log(`捕获到错误: ${error.name} - ${error.message}`); } } // 2. Node.js 系统错误 async function demonstrateSystemErrors() { console.log('\n=== Node.js 系统错误 ==='); try { // ENOENT - 文件或目录不存在 await fs.readFile('nonexistent-file.txt', 'utf8'); } catch (error) { console.log(`系统错误: ${error.code} - ${error.message}`); console.log(`错误路径: ${error.path}`); console.log(`系统调用: ${error.syscall}`); } try { // EACCES - 权限拒绝 await fs.access('/root/restricted-file', fs.constants.F_OK); } catch (error) { if (error.code === 'EACCES') { console.log('权限被拒绝'); } } } // 3. Promise 错误处理 async function demonstratePromiseErrors() { console.log('\n=== Promise 错误处理 ==='); // Promise.reject() 错误 try { await Promise.reject(new Error('Promise rejected')); } catch (error) { console.log(`Promise 错误: ${error.message}`); } // 未处理的 Promise rejection const unhandledPromise = Promise.reject(new Error('未处理的 Promise 错误')); // 捕获未处理的 rejection process.on('unhandledRejection', (reason, promise) => { console.log('未处理的 Promise rejection:', reason.message); console.log('Promise:', promise); // 在生产环境中应该记录错误并优雅关闭 // process.exit(1); }); } // 4. EventEmitter 错误处理 function demonstrateEventEmitterErrors() { console.log('\n=== EventEmitter 错误处理 ==='); const emitter = new EventEmitter(); // 监听错误事件 emitter.on('error', (error) => { console.log(`EventEmitter 错误: ${error.message}`); }); // 触发错误 emitter.emit('error', new Error('EventEmitter 发生错误')); // 如果没有错误监听器,会抛出错误 const emitter2 = new EventEmitter(); try { emitter2.emit('error', new Error('无监听器的错误')); } catch (error) { console.log(`未监听的 EventEmitter 错误: ${error.message}`); } } // 5. 自定义错误类 class CustomError extends Error { constructor(message, code, statusCode = 500) { super(message); this.name = 'CustomError'; this.code = code; this.statusCode = statusCode; // 保持堆栈跟踪 if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError); } } } class ValidationError extends CustomError { constructor(message, field) { super(message, 'VALIDATION_ERROR', 400); this.name = 'ValidationError'; this.field = field; } } function demonstrateCustomErrors() { console.log('\n=== 自定义错误类 ==='); try { throw new ValidationError('邮箱格式不正确', 'email'); } catch (error) { if (error instanceof ValidationError) { console.log(`验证错误: ${error.message}`); console.log(`错误字段: ${error.field}`); console.log(`状态码: ${error.statusCode}`); } } } // 6. 全局错误处理 function setupGlobalErrorHandlers() { console.log('\n=== 全局错误处理设置 ==='); // 捕获未处理的异常 process.on('uncaughtException', (error) => { console.error('未捕获的异常:', error); // 记录错误后优雅关闭 console.log('正在关闭服务器...'); // 在实际应用中,应该: // 1. 记录错误到日志系统 // 2. 通知监控系统 // 3. 优雅关闭服务器 // process.exit(1); }); // 捕获未处理的 Promise rejection process.on('unhandledRejection', (reason, promise) => { console.error('未处理的 Promise rejection:', reason); console.log('在 Promise:', promise); // 抛出异常让 uncaughtException 处理 // throw reason; }); // 进程退出处理 process.on('SIGINT', () => { console.log('\n收到 SIGINT 信号,正在优雅关闭...'); process.exit(0); }); process.on('SIGTERM', () => { console.log('收到 SIGTERM 信号,正在优雅关闭...'); process.exit(0); }); } // 7. 错误监控和日志记录 class ErrorMonitor { constructor() { this.errorCounts = new Map(); this.errorHistory = []; } logError(error, context = {}) { const errorInfo = { timestamp: new Date().toISOString(), name: error.name, message: error.message, stack: error.stack, code: error.code, context }; // 记录错误 this.errorHistory.push(errorInfo); // 统计错误次数 const key = `${error.name}:${error.message}`; this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1); console.log('错误已记录:', JSON.stringify(errorInfo, null, 2)); } getErrorStats() { return { totalErrors: this.errorHistory.length, errorCounts: Object.fromEntries(this.errorCounts), recentErrors: this.errorHistory.slice(-5) }; } } // 主函数 async function main() { console.log('Node.js 错误处理演示'); console.log('====================\n'); // 设置全局错误处理 setupGlobalErrorHandlers(); // 创建错误监控器 const monitor = new ErrorMonitor(); try { // 演示各种错误类型 demonstrateJSErrors(); await demonstrateSystemErrors(); await demonstratePromiseErrors(); demonstrateEventEmitterErrors(); demonstrateCustomErrors(); // 模拟一些错误并监控 setTimeout(() => { try { throw new Error('定时器错误'); } catch (error) { monitor.logError(error, { source: 'timer' }); } }, 1000); // 输出错误统计 setTimeout(() => { console.log('\n=== 错误统计 ==='); console.log(JSON.stringify(monitor.getErrorStats(), null, 2)); }, 2000); } catch (error) { monitor.logError(error, { source: 'main' }); } } // 执行演示 if (require.main === module) { main().catch(console.error); } module.exports = { CustomError, ValidationError, ErrorMonitor, demonstrateJSErrors, demonstrateSystemErrors, demonstratePromiseErrors, demonstrateEventEmitterErrors, demonstrateCustomErrors, setupGlobalErrorHandlers };
全局错误捕获:
// 捕获未处理的异常
process.on('uncaughtException', (error) => {
console.error('未捕获异常:', error);
process.exit(1);
});
// 捕获未处理的Promise rejection
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的Promise rejection:', reason);
process.exit(1);
});
自定义错误类:
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
this.statusCode = 400;
}
}
错误处理策略:
同步错误: 使用try-catch捕获 异步错误: 使用Promise.catch()或async/await + try-catch EventEmitter错误: 监听'error'事件 Stream错误: 使用pipeline()和错误处理中间件
面试官视角:
该题考察候选人的错误处理能力:
- 要点清单: 熟悉各种错误类型;掌握捕获方法;理解异步错误处理;能设计错误监控
- 加分项: 有生产环境错误处理经验;能设计错误恢复机制;了解错误监控系统;有错误分析能力
- 常见失误: 只了解基础错误类型;不理解异步错误;缺乏全局错误处理;忽视错误监控
延伸阅读:
- Node.js错误处理最佳实践 — 官方错误处理指南
- 《Node.js调试指南》 — 调试和错误排查
如何进行Node.js内存优化?
答案
核心概念:
Node.js内存优化涉及理解V8垃圾回收机制、识别内存泄漏、合理使用内存分配策略。掌握内存管理是构建高性能Node.js应用的关键。
V8垃圾回收机制:
分代回收策略:
- 新生代(New Space): 存放短期对象,使用Scavenge算法快速回收
- 老生代(Old Space): 存放长期对象,使用Mark-Sweep和Mark-Compact算法
垃圾回收过程:
- 新对象分配到新生代
- 经过2次垃圾回收仍存活的对象晋升到老生代
- 老生代空间不足时触发Mark-Sweep回收
- 内存碎片过多时执行Mark-Compact整理
内存优化实践:
- 内存优化演示
- 垃圾回收概念
// Node.js 内存优化示例 const { performance, PerformanceObserver } = require('perf_hooks'); // 1. 内存使用监控 function monitorMemoryUsage() { console.log('=== 内存使用监控 ==='); const usage = process.memoryUsage(); const formatBytes = (bytes) => (bytes / 1024 / 1024).toFixed(2) + ' MB'; console.log(`RSS (常驻内存): ${formatBytes(usage.rss)}`); console.log(`Heap Used (堆内存使用): ${formatBytes(usage.heapUsed)}`); console.log(`Heap Total (堆内存总计): ${formatBytes(usage.heapTotal)}`); console.log(`External (外部内存): ${formatBytes(usage.external)}`); console.log(`Array Buffers (数组缓冲区): ${formatBytes(usage.arrayBuffers)}`); // 计算内存使用率 const heapUsagePercent = ((usage.heapUsed / usage.heapTotal) * 100).toFixed(2); console.log(`堆内存使用率: ${heapUsagePercent}%`); return usage; } // 2. 垃圾回收监控 function setupGCMonitoring() { console.log('\n=== 垃圾回收监控 ==='); const obs = new PerformanceObserver((list) => { const entries = list.getEntries(); entries.forEach((entry) => { console.log(`GC ${entry.name}: ${entry.duration.toFixed(2)}ms`); console.log(`GC 类型: ${entry.detail ? entry.detail.kind : 'unknown'}`); }); }); obs.observe({ entryTypes: ['gc'] }); // 主动触发垃圾回收 (需要 --expose-gc 参数) if (global.gc) { console.log('手动触发垃圾回收...'); global.gc(); } else { console.log('需要 --expose-gc 参数来启用手动垃圾回收'); } return obs; } // 3. 内存泄漏演示和检测 class MemoryLeakDemo { constructor() { this.cache = new Map(); this.listeners = []; this.timers = []; } // 演示常见的内存泄漏场景 demonstrateMemoryLeaks() { console.log('\n=== 内存泄漏演示 ==='); // 1. 缓存无限增长 this.createUnboundedCache(); // 2. 事件监听器未移除 this.createUnremovedListeners(); // 3. 定时器未清理 this.createUnclearedTimers(); // 4. 闭包引用 this.createClosureReference(); } createUnboundedCache() { console.log('创建无界限缓存 (模拟内存泄漏)...'); // 模拟无限增长的缓存 for (let i = 0; i < 10000; i++) { this.cache.set(`key_${i}`, { data: new Array(100).fill(`value_${i}`), timestamp: Date.now() }); } console.log(`缓存大小: ${this.cache.size}`); } createUnremovedListeners() { console.log('创建未移除的事件监听器...'); const EventEmitter = require('events'); const emitter = new EventEmitter(); // 创建多个未移除的监听器 for (let i = 0; i < 100; i++) { const listener = () => console.log(`Listener ${i}`); emitter.on('test', listener); this.listeners.push({ emitter, event: 'test', listener }); } console.log(`创建了 ${this.listeners.length} 个事件监听器`); } createUnclearedTimers() { console.log('创建未清理的定时器...'); // 创建多个定时器但不清理 for (let i = 0; i < 50; i++) { const timer = setInterval(() => { // 模拟一些工作 const data = new Array(1000).fill(Math.random()); }, 100); this.timers.push(timer); } console.log(`创建了 ${this.timers.length} 个定时器`); } createClosureReference() { console.log('创建闭包引用...'); const largeData = new Array(10000).fill('large data chunk'); // 闭包持有大对象的引用 this.closureFunction = () => { return largeData.length; }; console.log('闭包函数已创建,持有大对象引用'); } // 清理内存泄漏 cleanup() { console.log('\n=== 清理内存泄漏 ==='); // 清理缓存 this.cache.clear(); console.log('缓存已清理'); // 移除事件监听器 this.listeners.forEach(({ emitter, event, listener }) => { emitter.removeListener(event, listener); }); this.listeners = []; console.log('事件监听器已清理'); // 清理定时器 this.timers.forEach(timer => clearInterval(timer)); this.timers = []; console.log('定时器已清理'); // 清理闭包引用 this.closureFunction = null; console.log('闭包引用已清理'); } } // 4. 内存优化最佳实践 class MemoryOptimizer { // 对象池模式 static createObjectPool(createFn, resetFn, initialSize = 10) { const pool = []; // 预填充对象池 for (let i = 0; i < initialSize; i++) { pool.push(createFn()); } return { get() { return pool.length > 0 ? resetFn(pool.pop()) : createFn(); }, release(obj) { if (pool.length < 100) { // 限制池大小 pool.push(obj); } }, size() { return pool.length; } }; } // 流式处理大文件 static async processLargeDataStream(data, batchSize = 1000) { console.log('\n=== 流式处理演示 ==='); const results = []; for (let i = 0; i < data.length; i += batchSize) { const batch = data.slice(i, i + batchSize); // 处理批次数据 const processed = batch.map(item => item * 2); results.push(...processed); // 在每个批次后检查内存 if (i % (batchSize * 10) === 0) { const usage = process.memoryUsage(); console.log(`处理进度: ${((i / data.length) * 100).toFixed(1)}%, 堆内存: ${(usage.heapUsed / 1024 / 1024).toFixed(2)}MB`); // 主动进行垃圾回收 if (global.gc) { global.gc(); } } } return results; } // 弱引用使用示例 static demonstrateWeakReferences() { console.log('\n=== WeakMap/WeakSet 使用示例 ==='); // 使用 WeakMap 避免内存泄漏 const weakCache = new WeakMap(); const strongCache = new Map(); class User { constructor(name) { this.name = name; } } const users = [ new User('Alice'), new User('Bob'), new User('Charlie') ]; // 使用 WeakMap 存储用户相关数据 users.forEach(user => { weakCache.set(user, { loginCount: 0, lastLogin: Date.now() }); strongCache.set(user.name, user); }); console.log(`WeakMap 大小: ${weakCache.has(users[0]) ? '包含用户数据' : '不包含用户数据'}`); console.log(`强引用缓存大小: ${strongCache.size}`); // 清除用户引用后,WeakMap 中的数据会被自动回收 users.length = 0; // 强制垃圾回收 if (global.gc) { global.gc(); } console.log('用户引用清除后,WeakMap 数据将被自动回收'); } } // 5. 内存监控工具 class MemoryMonitor { constructor(interval = 5000) { this.interval = interval; this.monitoring = false; this.history = []; } start() { if (this.monitoring) return; this.monitoring = true; console.log(`开始内存监控,间隔: ${this.interval}ms`); this.timer = setInterval(() => { const usage = process.memoryUsage(); const timestamp = Date.now(); this.history.push({ timestamp, usage }); // 保持历史记录在合理范围内 if (this.history.length > 100) { this.history.shift(); } // 检测内存增长趋势 this.checkMemoryGrowth(); }, this.interval); } stop() { if (this.timer) { clearInterval(this.timer); this.monitoring = false; console.log('内存监控已停止'); } } checkMemoryGrowth() { if (this.history.length < 5) return; const recent = this.history.slice(-5); const first = recent[0].usage.heapUsed; const last = recent[recent.length - 1].usage.heapUsed; const growthRate = ((last - first) / first) * 100; if (growthRate > 10) { console.warn(`警告: 内存增长过快 ${growthRate.toFixed(2)}%`); this.logCurrentUsage(); } } logCurrentUsage() { const usage = process.memoryUsage(); console.log('当前内存使用:'); console.log(` RSS: ${(usage.rss / 1024 / 1024).toFixed(2)} MB`); console.log(` Heap Used: ${(usage.heapUsed / 1024 / 1024).toFixed(2)} MB`); console.log(` Heap Total: ${(usage.heapTotal / 1024 / 1024).toFixed(2)} MB`); } getStats() { if (this.history.length === 0) return null; const usages = this.history.map(h => h.usage.heapUsed); const min = Math.min(...usages); const max = Math.max(...usages); const avg = usages.reduce((a, b) => a + b, 0) / usages.length; return { samples: this.history.length, minHeapUsed: (min / 1024 / 1024).toFixed(2) + ' MB', maxHeapUsed: (max / 1024 / 1024).toFixed(2) + ' MB', avgHeapUsed: (avg / 1024 / 1024).toFixed(2) + ' MB' }; } } // 主函数演示 async function main() { console.log('Node.js 内存优化演示'); console.log('=====================\n'); // 1. 监控初始内存使用 const initialUsage = monitorMemoryUsage(); // 2. 设置垃圾回收监控 const gcObserver = setupGCMonitoring(); // 3. 演示内存泄漏 const leakDemo = new MemoryLeakDemo(); leakDemo.demonstrateMemoryLeaks(); console.log('\n内存泄漏创建后:'); monitorMemoryUsage(); // 4. 清理内存泄漏 setTimeout(() => { leakDemo.cleanup(); if (global.gc) { global.gc(); } console.log('\n清理后:'); monitorMemoryUsage(); }, 2000); // 5. 演示最佳实践 setTimeout(async () => { // 对象池示例 const numberPool = MemoryOptimizer.createObjectPool( () => ({ value: 0 }), (obj) => { obj.value = 0; return obj; } ); console.log('\n=== 对象池演示 ==='); const obj = numberPool.get(); obj.value = 42; numberPool.release(obj); console.log(`对象池大小: ${numberPool.size()}`); // 流式处理演示 const largeArray = new Array(50000).fill(0).map((_, i) => i); await MemoryOptimizer.processLargeDataStream(largeArray); // 弱引用演示 MemoryOptimizer.demonstrateWeakReferences(); }, 3000); // 6. 启动内存监控 const monitor = new MemoryMonitor(1000); monitor.start(); // 10秒后停止监控并输出统计 setTimeout(() => { monitor.stop(); const stats = monitor.getStats(); console.log('\n=== 内存监控统计 ==='); console.log(JSON.stringify(stats, null, 2)); gcObserver.disconnect(); process.exit(0); }, 10000); } // 执行演示 if (require.main === module) { main().catch(console.error); } module.exports = { monitorMemoryUsage, setupGCMonitoring, MemoryLeakDemo, MemoryOptimizer, MemoryMonitor };
内存区域划分:
- 新生代内存区: 大多数对象分配,垃圾回收频繁但快速
- 老生代指针区: 包含指向其他对象的指针对象
- 老生代数据区: 只保存原始数据,无指针的对象
- 大对象区: 超大对象独立存储
- 代码区: 编译后的代码对象
Scavenge算法:
From区 → To区 (复制存活对象)
交换From区和To区角色
清理原From区
Mark-Sweep算法:
标记阶段: 从根对象开始标记所有可达对象
清除阶段: 清理未标记的对象
常见内存泄漏场景:
- 无界限缓存增长: Map/Object缓存无清理机制
- 事件监听器累积: EventEmitter监听器未移除
- 定时器泄漏: setInterval/setTimeout未清理
- 闭包引用: 闭包持有大对象引用
优化策略:
- 使用对象池: 复用对象减少GC压力
- 流式处理: 处理大数据时使用流避免全量加载
- 弱引用: 使用WeakMap/WeakSet避免意外引用
- 定期清理: 设置缓存过期和清理机制
- 监控告警: 监控内存使用趋势设置告警
内存调优工具:
--inspect- Chrome DevTools调试--trace-gc- 跟踪垃圾回收--max-old-space-size- 调整老生代内存限制heapdump- 生成内存快照分析
面试官视角:
该题考察候选人的内存管理能力:
- 要点清单: 理解V8垃圾回收机制;能识别内存泄漏;掌握优化方法;有监控经验
- 加分项: 深入理解GC算法;有大型应用内存调优经验;能设计内存监控系统;了解性能分析工具
- 常见失误: 只知道表面概念;不理解GC原理;缺乏实际调优经验;忽视内存监控
延伸阅读:
- V8垃圾回收机制详解 — V8官方垃圾回收原理
- Node.js内存泄漏排查指南 — 内存问题排查方法