JVM 的 Full GC 是一个比较重的操作,它会对整个堆内存进行垃圾回收,包括新生代和老年代。如果 Full GC 频繁发生,会对应用的性能产生很大影响,比如导致系统停顿时间变长、响应变慢等。下面我将结合一个电商系统的例子,来详细讲解 JVM 的 Full GC 以及 JVM 发展史上为了避免 Full GC 所做的努力。
Full GC 的触发及影响
假设有一个电商系统,用户在浏览商品、下单购物等操作时,系统会产生大量的临时对象,比如商品信息对象、订单对象等。这些对象很多都是短期存在的,但有一部分会存活下来,晋升到老年代。
一开始,新生代的 Eden 区和 Survivor 区存放着大量新创建的对象。当 Eden 区满了之后,就会触发 Minor GC。Minor GC 会将存活下来的对象移动到 Survivor 区,经过多次 Minor GC 后,如果对象仍然存活,就会晋升到老年代。但如果老年代的空间不足,无法容纳这些晋升的对象,就会触发 Full GC。比如系统在大促活动期间,流量剧增,大量订单对象产生,老年代空间很快被占满,就会频繁触发 Full GC。
Full GC 的触发会导致整个应用暂停,等待垃圾回收完成,这期间用户的请求无法得到及时响应,比如用户提交的订单可能长时间没有反馈,页面加载缓慢等,严重影响用户体验。
JVM 发展史上避免 Full GC 的努力
-
垃圾回收器的改进 :
- Serial Old 收集器 :它是 JDK1.2 伴随着 Serial 收集器首次出现的老年代收集器,只能在老年代半空间可用时使用,只支持单线程垃圾回收。在早期的 JVM 版本中,对于一些小型应用或者对性能要求不高的场景,它也能基本满足需求,但由于其单线程的特点,在面对大规模数据和高并发场景时,垃圾回收的效率较低,容易导致较长的停顿时间。
- Parallel Old 收集器 :它是 Parallel 收集器的老年代版本,支持多线程垃圾回收,关注吞吐量。在 JDK1.6 后期推出的版本中,Parallel Old 收集器可以在短时间内回收大量的垃圾对象,减少了垃圾回收的总时间,提高了应用的整体吞吐量,但可能会导致较长的停顿时间,因为它需要暂停用户的应用程序来执行垃圾回收操作。
- CMS 收集器 :即并发低停顿收集器,它可以在不停止用户线程的情况下执行垃圾回收,大大减少了垃圾回收的停顿时间,提高了应用的响应速度。它主要分为四个阶段:初始标记、并发标记、重新标记和并发清除。初始标记阶段会短暂停顿用户线程,而并发标记阶段会和用户线程同时执行,重新标记阶段会