minor gc仅清理年轻代(eden+1个survivor),eden满即触发,stw约1–50ms;major gc非标准术语,通常指仅回收老年代;full gc清理整堆+元空间,stw可达百毫秒至数秒,触发原因包括老年代/元空间不足、system.gc()等。

Minor GC、Major GC 和 Full GC 的核心区别不在名字长短,而在作用范围、触发逻辑和停顿代价。理解清楚这三者,才能快速定位 GC 问题。
Minor GC:只动年轻代,Eden 满就出发
Minor GC 的触发条件非常聚焦:Eden 区空间不足是主因。新对象分配时发现 Eden 剩余空间不够,立刻启动——此时哪怕 Survivor 区空着 90%,也不会提前回收。
次要但实际存在的路径包括:
- TLAB 分配失败后,回退到 Eden 全局分配仍失败
- 大对象尝试直接进入老年代失败,先触发 Minor GC 尝试腾空间
- 对象年龄达到阈值(默认 15)或 Survivor 同龄对象超 50%,需晋升,但晋升本身不触发 Minor GC,只是为后续 GC 埋点
它只清理 Eden + 一个 Survivor 区,采用复制算法,STW 时间通常在 1–50 毫秒,对响应敏感型服务影响相对可控。
Major GC:非标准术语,实际多指老年代单独回收
“Major GC” 并非 JVM 规范定义的正式类型,不同团队可能用它指代不同行为。常见理解有两种:
- 仅回收老年代(如 CMS 的并发收集阶段),不碰年轻代和元空间
- 等同于 Full GC,尤其在早期 JDK 文档或部分监控工具中
真正稳定可依赖的判断依据是:如果 GC 日志里显示只扫描了老年代(Old),且没有 Young 或 Metaspace 相关动作,那基本就是狭义上的 Major GC。它频率低、耗时长,常采用标记-清除或标记-整理算法。
Full GC:整堆+元空间一锅端,停顿最重
Full GC 是唯一明确覆盖全部区域的 GC 类型:新生代 + 老年代 + 元空间(Metaspace)。只要以下任一条件满足,就可能触发:
- 老年代连续可用空间不足,无法容纳晋升对象或大对象
- 元空间使用量超过 -XX:MaxMetaspaceSize(常见于 Spring AOP、CGLib 动态代理、热部署未清理 ClassLoader)
- 显式调用 System.gc(),且未配置 -XX:+DisableExplicitGC
- Minor GC 前的“晋升担保检查”失败:老年代最大连续空闲空间
- G1 发生 Mixed GC 跟不上分配速度,或 CMS 出现 Concurrent Mode Failure,被迫降级为单线程 Full GC
它的 STW 时间通常是 Minor GC 的 10 倍起步,百毫秒到数秒不等,期间 JIT 编译、类加载、Unsafe 内存分配等全部冻结。
Full GC 频繁?从这四类线索快速排查
高频 Full GC 不是随机发生,背后一定有明确诱因。优先检查以下方向:
-
元空间是否泄漏:用 jstat -gc
看 MCM/MCU 值持续上涨;jcmd VM.native_memory summary 查看 metaspace 分配趋势;dump classloader(jcmd VM.class_hierarchy -all)确认是否存在重复加载的类 - 老年代是否被缓慢填满:观察 GC 日志中每次 Full GC 前老年代使用率是否阶梯上升;检查是否有缓存(如 HashMap、ConcurrentHashMap)长期持有对象未清理,或静态集合不断 add
- 是否存在隐式 System.gc():RMI 默认每小时调用一次;某些 NIO 框架、数据库连接池、监控 SDK 可能内置该调用;加 JVM 参数 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 并搜索 “System.gc()” 字样
- 大对象或晋升策略是否失当:查看日志中是否有 “promotion failed” 或 “to-space overflow”;检查 -XX:PretenureSizeThreshold 是否过小导致本该直入老年代的大数组反复在年轻代拷贝;Survivor 区太小或 MaxTenuringThreshold 设置不合理,造成对象过早或过晚晋升
不复杂但容易忽略:一次 Full GC 往往是多个小问题叠加的结果,比如元空间缓慢增长 + 老年代碎片化 + 一次大日志写入,共同压垮了临界点。










