Java垃圾回收需从对象生死逻辑入手,通过可达性分析判定垃圾,结合分代假说理解内存布局与算法选择,再通过日志和工具实战调优排查问题。

Java虚拟机垃圾回收不是靠死记硬背学会的,得从“对象怎么活、怎么死、在哪死、怎么高效地死”这条主线一层层拆解。学得扎实的人,往往能把GC参数调优和线上问题排查串起来,而不是只会背“Minor GC发生在年轻代”这种结论。
先搞懂对象到底怎么被判定为垃圾
JVM不用引用计数法,而是用可达性分析:从GC Roots出发,沿着引用链往下找,找不到的对象才算真正可回收。哪些能当GC Roots?栈帧里的局部变量、静态字段、常量池中的常量、JNI引用的对象——这些必须牢牢记住,因为它们直接决定你写的代码里哪些对象“看似null了”,其实还被悄悄挂着。比如静态集合类长期持有对象引用,就是典型的内存泄漏源头。
分代设计是理解GC行为的关键入口
堆被划成年轻代(Eden + 2个Survivor)和老年代,不是为了好看,而是基于两个事实:
- 大多数对象朝生夕死(弱分代假说)→ 年轻代用复制算法,快且无碎片
- 活得久的对象大概率一直活着(强分代假说)→ 老年代用标记-整理,省空间、防碎片
所以你看到-XX:NewRatio=2,就知道年轻代占堆1/3;看到-XX:SurvivorRatio=8,就明白Eden和一个Survivor是8:1的关系。参数背后全是逻辑,不是魔法数字。
别一上来就调G1或ZGC,先吃透Serial、Parallel、CMS的取舍
- Serial:单线程,STW明显,适合小应用或嵌入式
- Parallel(吞吐量优先):多线程但STW更长,适合后台批处理
- CMS(停顿时间优先):并发标记清除,但有浮动垃圾和Concurrent Mode Failure风险
- G1:把堆分成Region,可预测停顿,适合大堆(4G+)和延迟敏感场景
选哪个,取决于你的业务特征:是CPU密集型还是IO密集型?能接受200ms停顿还是必须50ms内?有没有大量短生命周期大对象?这些问题比“哪个收集器最新”重要得多。
动手环节不能跳过
- 用
-XX:+PrintGCDetails -Xloggc:gc.log打开GC日志,看懂每行含义:[GC (Allocation Failure)说明是Eden满了触发Minor GC;[Full GC (Metadata GC Threshold)则指向元空间快爆了 - 用
jstat -gc实时观察各代使用率和GC次数,发现Eden区每次回收后Survivor总塞不满,可能说明对象晋升太快,该调-XX:MaxTenuringThreshold了 - 故意写个内存泄漏demo:往static HashMap里不断put新对象,再用jvisualvm连上去看老年代持续上涨,直观理解“为什么静态引用这么危险”
真正掌握GC,是你看到Full GC频繁发生时,第一反应不是重启服务,而是查日志→看晋升年龄→检查大对象分配→翻代码里有没有缓存没清理。这个过程跑通一次,比读十篇原理文章都管用。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











