什么时候会进行Minor GC?什么时候FULL GC?

博客主要围绕Java面试中常问的Minor GC和Full GC触发时机展开。JVM采用标记 - 复制法进行垃圾回收,对象先进入S区,S区满时进行Minor GC。Full GC触发与对象大小、年轻代和老年代空闲内存等因素有关,若处理不当可能导致内存溢出。

在面试中通常会被问到,什么时候会进行Minor GC?什么时候FULL GC?

进行Minor GC

首先我们说说垃圾回收,jvm采用的垃圾回收规则采用的时 标记-复制法
一个Survivor以及两个Eden。
对象刚进来时会在S区,当S区满了后,无法创建对象时,会进行Minor GC,将存活的对象移动到一个Eden区。然后完成Minor Gc

进行Full Gc

继上,当MinorGc后,内存空出来后,对象进来后继续向S区以及E区创建对象。当对象满了后继续重复上述的操作。
然而,在每一次的MinorGc之前,会将对象的大小、年轻代剩余内存大小、老年代空余内存的大小进行对比

  • 如果这个对象大于年轻代的空闲内存(即Survivor以及Eden)且小于老年代空闲,可以直接进行FullGc(因为极端情况下这批对象都存货的)
  • 如果此对象大于老年代内存,则比较以往MinorGc后的平均存活对象大小:
    1 .如果存活对象内存小于平均水平,且小于S区,直接进去S区。如果小于平均水平,且大于S区,直接进入老年代
    2 .如果存活对象内存大于平均水平,之后判断,参数“-XX:-
    HandlePromotionFailure”,是否设置,如果没设置直接进行Full Gc,如果设置了,则冒险进行MinorGc。MinorGc有几种情况:
  • 1 MinorGc后存货对象,小于S区。那么此时存活对象进入Survivor
    区域即可。
  • 2 Minor GC过后,剩余的存活对象的大小,是大于 Survivor区域的大小,但是是小于老年代可用内存大小的,此时就直接进入老年代即可。
  • 3 大于老年代内存的,直接报内存溢出即可(即Handle Promotion Failure情况)。

如果要是Full GC过后,老年代还是没有足够的空间存放Minor GC过后的剩余存活对象,那么此时就会导致所谓的“OOM”内存溢出了

### 什么是 Full GC? **Full GC** 是 Java 垃圾回收中的一种**全局回收行为**,它会回收整个 Java 堆(包括新生代和老年代)以及方法区(元空间或永久代)中的垃圾对象。 Full GC 的触发条件通常包括以下几种: 1. **老年代空间不足**:当新生代对象晋升到老年代时,老年代空间不足以容纳这些对象。 2. **System.gc() 被调用**:显式调用 `System.gc()` 会触发 Full GC(除非使用 `-XX:+DisableExplicitGC` 禁止)。 3. **方法区(元空间)不足**:加载大量类或动态生成类时,元空间不足会触发 Full GC。 4. **CMS 并发模式失败**:在使用 CMS 回收器时,如果并发清理过程中老年代空间不足,会发生并发失败,触发 Full GC。 5. **G1 的 Mixed GC 失败**:如果 G1 在混合回收阶段无法回收足够的空间,也可能触发 Full GC。 --- ### Full GC 的执行过程(以 Serial 收集器为例) 1. **Stop-The-World(STW)**:JVM 暂停所有用户线程。 2. **标记存活对象**:从 GC Roots 开始标记所有存活对象。 3. **清除垃圾对象**:回收未被标记的对象。 4. **整理内存(可选)**:某些收集器(如 Serial Old、Parallel Old)会在 Full GC 后进行内存整理,以减少内存碎片。 --- ### 频繁 Full GC 会带来什么影响? 频繁 Full GC 是性能调优中非常严重的问题,主要影响如下: #### 1. **响应时间变长** - 每次 Full GC 都会导致**Stop-The-World(STW)**,暂停所有用户线程。 - Full GC 的时间通常比 Minor GC 长很多(可能几十毫秒到几秒),影响用户体验。 #### 2. **吞吐量下降** - Full GC 占用了大量 CPU 时间,减少了真正用于业务处理的时间。 - 如果 Full GC 频率高,吞吐量显著下降。 #### 3. **内存抖动和 OOM 风险** - 频繁 Full GC 往往意味着堆内存不足或内存泄漏。 - 如果不能及时释放内存,最终可能导致 `java.lang.OutOfMemoryError`。 #### 4. **系统不稳定** - Full GC 期间线程暂停,可能导致: - 接口超时 - 心跳丢失 - 分布式系统误判节点宕机 - 数据不一致 --- ### 示例:如何通过 GC 日志判断 Full GC 频繁? GC 日志示例(启用方式:`-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps`): ```text 2025-04-05T10:00:00.123+0800: [Full GC (System.gc()) [PSYoungGen: 102400K->0K(102400K)] [ParOldGen: 204800K->184320K(204800K)] 307200K->184320K(307200K), [Metaspace: 34560K->34560K(1069056K)], 0.8765432 secs] [Times: user=0.88 sys=0.00, real=0.88 secs] ``` 如果看到大量 `[Full GC ...]` 记录,说明 Full GC 频繁。 --- ### 如何解决频繁 Full GC? #### 1. **分析 GC 日志** - 使用工具如 [GCEasy](https://gceasy.io/)、GCViewer、VisualVM 等分析 GC 日志。 - 查看 Full GC 的触发原因、频率、耗时。 #### 2. **增加堆内存** ```bash java -Xms4g -Xmx4g -jar yourapp.jar ``` #### 3. **避免显式调用 System.gc()** - 使用 JVM 参数禁用: ```bash -XX:+DisableExplicitGC ``` #### 4. **优化代码** - 避免创建大量短生命周期对象。 - 及时释放资源(关闭流、连接池归还等)。 - 避免内存泄漏(如缓存未清理、监听器未注销等)。 #### 5. **使用更高效的垃圾回收器** - 使用 G1、ZGC、Shenandoah 等现代 GC,减少 Full GC 频率。 --- ### 示例代码:使用 JMX 获取 GC 次数 ```java import java.lang.management.GarbageCollectorMXBean; import java.util.List; import java.lang.management.ManagementFactory; public class FullGCCounter { public static void main(String[] args) { List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); for (GarbageCollectorMXBean gc : gcBeans) { System.out.println("GC Name: " + gc.getName() + ", Count: " + gc.getCollectionCount()); } } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值