Java垃圾回收基于可达性分析算法判定对象是否存活,从GC Roots出发搜索引用链,不可达对象即被回收;堆分新生代(Eden/Survivor)和老年代,依分代假说优化回收效率;触发条件包括Eden区满(Minor GC)、老年代空间不足(Full GC)、System.gc()建议及元空间告警等。

Java 垃圾回收(GC)不是后台定时清扫的“保洁员”,而是由 JVM 根据内存压力和运行状态动态决策的智能回收系统。它不保证立刻执行,也不按固定周期运行,关键在于“什么时候该收”和“收哪里”。理解它的基础原理和触发条件,才能避开常见性能陷阱。
对象怎么被判定为“垃圾”?
JVM 不靠引用计数(会卡在循环引用上),而是用可达性分析算法:从一组称为 GC Roots 的对象出发,沿着引用链向下搜索。如果一个对象到所有 GC Roots 都不可达,就认定为可回收。
常见的 GC Roots 包括:
- 虚拟机栈中正在使用的局部变量引用的对象
- 方法区中类的静态属性引用的对象
- 方法区中的常量(比如字符串常量池里的字符串)引用的对象
- 本地方法栈中 JNI(Native 方法)引用的对象
堆内存是怎么分区的?为什么分代?
JVM 把堆划分为新生代和老年代,依据是“分代假说”——大部分对象朝生暮死,少数长期存活。这种划分让 GC 更高效:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- 新生代(默认占堆 1/3):又细分为 Eden 区(新对象首选)、两个 Survivor 区(S0/S1)。对象先在 Eden 分配,Minor GC 后存活对象进入 Survivor,并记录“年龄”;达到阈值(默认 15 次 GC)后晋升老年代
- 老年代(默认占堆 2/3):存放长期存活对象、大对象(直接分配)或 Survivor 区放不下的对象。Major GC 或 Full GC 主要清理这里
- 元空间(Metaspace):Java 8+ 替代永久代,存类元数据;虽不属堆,但耗尽时也可能间接触发 Full GC
GC 在什么情况下会被触发?
GC 触发是 JVM 的自主决策,常见场景有四类:
- Eden 区空间不足:创建新对象时发现 Eden 没空位,立即触发 Minor GC;若 Survivor 无法容纳存活对象,或老年代剩余空间不足担保晋升,就会升级为 Full GC
- 老年代空间不足:Minor GC 后对象晋升失败、大对象直接分配失败、或老年代本身使用率过高,都会触发 Major GC 或 Full GC
- 显式调用 System.gc():只是向 JVM 发出建议,JVM 可忽略;频繁调用反而干扰自动策略,不推荐用于生产环境
- 元空间或直接内存告警:比如动态生成大量类(反射、CGLib)、或堆外内存(ByteBuffer.allocateDirect)耗尽,可能触发 Full GC 尝试腾挪资源
不同 GC 类型有什么区别?
类型名称和影响范围需分清:
- Minor GC:只清理新生代(Eden + Survivor),频率高、停顿短,是日常最常发生的 GC
- Major GC:专指清理老年代,实际中较少单独发生,常与 Minor GC 连动或合并为 Full GC
- Full GC:清理整个堆(新生代 + 老年代)+ 元空间,STW 时间长、影响大;典型诱因包括老年代满、System.gc()、CMS 失败、元空间扩容失败等
GC 是自动的,但不是无感的。掌握这些基础逻辑,配合 -XX:+PrintGCDetails 等参数观察日志,就能把内存问题从“黑盒”变成可推演、可调优的过程。

















