内存优化
概览
内存优化提升应用性能、稳定性,并降低基础设施成本。高效的内存使用对可扩展性至关重要。
使用场景
- 高内存使用
- 怀疑内存泄漏
- 性能缓慢
- 内存溢出崩溃
- 扩展挑战
指南
1. 内存分析
// 浏览器内存分析
// 检查内存使用情况
performance.memory: {
jsHeapSizeLimit: 2190000000, // 最大可用
totalJSHeapSize: 1300000000, // 总分配
usedJSHeapSize: 950000000 // 当前使用
}
// React DevTools 分析器
- 打开 React DevTools → 分析器
- 记录交互
- 查看组件渲染和时间
- 识别不必要的渲染
// Chrome DevTools
1. 打开 DevTools → 内存
2. 拍摄堆快照
3. 比较前后
4. 查找保留对象
5. 检查保留大小
// Node.js 分析
node --inspect app.js
// 打开 chrome://inspect
// 拍摄堆快照
// 随时间比较增长
2. 内存泄漏检测
# 识别和修复内存泄漏
class MemoryLeakDebug:
def identify_leaks(self):
"""常见模式"""
return {
'circular_references': {
'问题': '对象相互引用,阻止垃圾回收',
'示例': 'parent.child = child; child.parent = parent',
'解决方案': '使用弱引用或更清晰的代码'
},
'event_listeners': {
'问题': '监听器未移除',
'示例': 'element.addEventListener(...) 没有 removeEventListener',
'解决方案': '总是在清理时移除监听器'
},
'timers': {
'问题': 'setInterval/setTimeout 未清除',
'示例': 'setInterval(() => {}, 1000) 从未 clearInterval',
'解决方案': '存储 ID 并在卸载时清除'
},
'cache_unbounded': {
'问题': '缓存无限制增长',
'示例': 'cache[key] = value (从未删除)',
'解决方案': '实现 TTL 或大小限制'
},
'dom_references': {
'问题': '已移除的 DOM 元素仍被引用',
'示例': 'var x = document.getElementById("removed")',
'解决方案': '移除后清除引用'
}
}
def detect_in_browser(self):
"""JavaScript 检测"""
return """
// 监控内存增长
setInterval(() => {
const mem = performance.memory;
const used = mem.usedJSHeapSize / 1000000;
console.log(`Memory: ${used.toFixed(1)} MB`);
}, 1000);
// 如果随时间增长而无平台 = 泄漏
"""
3. 优化技术
内存优化:
对象池:
模式:重用对象而不是创建新对象
示例:游戏中的游戏对象池
好处:减少垃圾回收,稳定内存
权衡:复杂性
懒加载:
模式:仅在需要时加载数据
示例:无限滚动
好处:降低峰值内存
权衡:复杂性
分页:
模式:分块处理数据
示例:1M 记录 → 每页 1K
好处:恒定内存
权衡:更多请求
流处理:
模式:一次处理一个项目
示例:fs.createReadStream()
好处:大数据恒定内存
权衡:如果缓存则更慢
记忆化:
模式:缓存昂贵的计算
好处:更快,重用结果
权衡:速度换内存
---
框架特定:
React:
- useMemo 用于昂贵的计算
- useCallback 避免创建函数
- 代码分割 / 懒加载
- 长列表的窗口化 (react-window)
Node.js:
- 流而不是 loadFile
- 限制集群工作
- 设置堆大小:--max-old-space-size=4096
- 用 clinic.js 监控
---
GC (垃圾回收):
最小化:
- 对象创建
- 大型分配
- 频繁的新对象
- 字符串连接
示例(不好):
let result = "";
for (let i = 0; i < 1000000; i++) {
result += i.toString() + ",";
// 每次迭代创建新字符串
}
示例(好):
const result = Array.from(
{length: 1000000},
(_, i) => i.toString()
).join(",");
// 单次分配
4. 监控 & 目标
内存目标:
Web 应用:
初始:<10MB
使用后:<50MB
峰值:<100MB
泄漏检查:应平台
Node.js API:
每个进程:100-500MB
集群总计:1-4GB
堆大小:监控与可用 RAM
移动:
初始:<20MB
工作:<50MB
峰值:<100MB(设备依赖)
---
工具:
浏览器:
- Chrome DevTools 内存
- Firefox DevTools 内存
- React DevTools 分析器
- Redux DevTools
Node.js:
- node --inspect
- clinic.js
- nodemon --exec 与监控
- New Relic / DataDog
监控:
- 应用性能监控 (APM)
- Prometheus + Grafana
- CloudWatch
- New Relic
---
清单:
[ ] 建立基线内存
[ ] 识别重组件
[ ] 在清理时移除事件监听器
[ ] 在清理时清除定时器
[ ] 实施懒加载
[ ] 对大型列表使用分页
[ ] 监控内存趋势
[ ] 设置 GC 监控
[ ] 用生产数据量测试
[ ] 压力测试泄漏
[ ] 建立内存预算
[ ] 设置警报
要点
- 建立基线内存测量
- 使用分析器识别问题
- 在清理时移除监听器和定时器
- 对大数据实施流处理
- 使用懒加载和分页
- 监控 GC 暂停时间
- 为工作负载设置适当的堆大小
- 对频繁分配使用对象池
- 定期用真实数据进行内存测试
- 对内存增长趋势设置警报