堆与 GC 入门:对象怎么分配?为什么会 OOM?怎么排查?

发布时间:2026/7/4 0:00:45

堆与 GC 入门:对象怎么分配?为什么会 OOM?怎么排查? 你在业务里最常见的 JVM 异常之一OutOfMemoryError: Java heap space但“堆”不是一个黑盒它至少有三件事你必须搞懂对象怎么进堆、怎么活下来GC 为什么要分代OOM 到底是“大对象瞬间打爆”还是“泄露慢慢涨”1. 堆是什么几乎所有对象的出生地在 HotSpot 里绝大多数对象分配在堆上。例外你可以先忽略逃逸分析/标量替换之类优化先把主线抓住对象创建 - 堆上分配 - 引用在栈里传来传去 - 垃圾回收器回收不可达对象2. 为什么要分代大部分对象“活不久”GC 分代的经验规律绝大多数对象朝生夕死请求对象、临时集合、DTO 等因此把堆分成新生代专门收短命对象回收频繁但成本低老年代长期存活对象回收不频繁但一次成本高你只要理解这个策略就能解释很多 GC 现象。3. 一次对象分配的典型旅程主线版对象优先在新生代分配Minor GC回收新生代多次 Minor GC 后仍存活的对象会晋升到老年代老年代压力大时触发 Major/Full GC具体与收集器实现相关4. 两类 OOM你要先分清4.1 瞬时型大对象/突发流量把堆打爆现象业务突发流量堆瞬间被顶满Full GC 也回收不下来4.2 渐进型内存泄露现象堆使用率慢慢爬Full GC 后仍然回不到低位最终 OOM5. 实战一个最小排障闭环建议你背下来5.1 第一步确认堆配置与实时占用看启动参数jcmd pid VM.command_line看堆信息jcmd pid GC.heap_info5.2 第二步看对象直方图快速抓“大户”jcmd pid GC.class_histogram你关注哪些类实例数/总大小异常是否出现你业务里不该无限增长的缓存对象5.3 第三步必要时堆转储做根因定位jmap -dump:formatb,fileheap.hprof pid然后用Eclipse MAT / IDEA Profiler 分析重点找Dominator TreeGC Roots 到可疑对象的引用链6. 常见导致泄露的“写法模式”你不需要记所有工具先记常见坑静态集合缓存不淘汰ThreadLocal使用不当没 remove监听器/回调注册不注销大对象缓存如大字符串、byte[]7. JDK 1.7 与 JDK 1.8你至少要知道的差异点这篇主要讲堆但你要知道一个经常混淆的点JDK 1.7类元数据在PermGenJDK 1.8类元数据在Metaspace所以看到PermGen space/Metaspace不要误判成堆 OOM8. 总结堆是对象主要分配区GC 主战场分代是为了高效回收短命对象OOM 先分清突发型 vs 泄露型排障最小闭环heap_info - class_histogram - dump MAT

相关新闻