V8分代回收将堆内存分为新生代(Scavenge算法,高频回收短命对象)和老生代(Mark-Sweep/Mark-Compact,低频回收长存对象),对象晋升条件为经历两次Scavenge或To空间占用超25%。

分代回收不是黑箱,而是能直接影响你每行代码性能的底层机制。理解它,就能避开高频GC陷阱,让页面更顺滑。
新生代对象别“随手乱造”
V8把短期存活的对象(比如函数内临时变量、循环里生成的数组)放进新生代,用Scavenge算法快速回收——但前提是它们真能“短命”。一旦频繁在循环或高频回调中创建对象、函数或数组,新生代内存很快爆满,触发高频率Scavenge,哪怕每次只停几毫秒,叠加起来就是卡顿。
- 避免在for循环里写 const item = {id: i, name: 'xxx'};改用复用对象或预分配数组
- 不用箭头函数闭包包裹大量数据(如 list.map(x => () => x.data)),这会让本该销毁的x被意外持有
- 字符串拼接少用 +=,尤其在长循环中,它会反复创建新字符串对象,推高新生代压力
老生代对象要“稳住引用链”
老生代存放长期存活对象(如全局状态、缓存、DOM引用),回收成本高(Mark-Sweep + Mark-Compact)、STW时间长。如果一个本该短期存在的对象因闭包、事件监听或意外全局赋值被拖进老生代,它不仅占内存,还会拖慢整轮GC。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 定时器、Observer、addEventListener 后,务必在组件卸载或逻辑结束时显式清理(clearTimeout、unobserve、removeEventListener)
- 避免无意中把局部变量挂到 window 或模块顶层(如漏写 let/const 直接赋值),这类变量直接晋升老生代且永不释放
- 大型数据缓存(如API响应)加生命周期控制,比如用 WeakMap 关联DOM节点,节点销毁后自动解绑,不阻碍回收
主动帮引擎判断“谁该死”
GC靠可达性分析:从根对象(global、调用栈、DOM)出发,能触达的就是“活”的。你写的每一条引用,都在悄悄延长对象寿命。
立即学习“Java免费学习笔记(深入)”;
- 不再需要的大数组、大对象,手动设为 null(如 bigData = null),切断引用链,让引擎立刻识别为垃圾
- 闭包中只捕获真正需要的变量,避免 function outer() { const a = bigObj; const b = tinyVal; return () => b; } —— 这样 a 也会被闭包持有
- 用 const 声明不重赋值的引用,既语义清晰,也向引擎传递“此引用不会变更”的信号,利于优化
用工具验证你的习惯是否生效
光靠理论不够,得看真实内存行为:
- Chrome DevTools → Memory 面板,录制一次操作后点 Collect garbage,观察堆大小回落是否明显;再对比未优化前的“内存增长斜率”
- Performance 面板录制滚动/交互,关注 Garbage Collection 事件的频率和耗时,若老生代GC频繁出现且单次>20ms,大概率有对象误入老生代
- 使用 console.memory 在关键节点打印已使用内存,辅助定位泄漏点

















